From 11d38a75ed91e2cdd76d18fb0bba4b27124bea3f Mon Sep 17 00:00:00 2001
From: "microsoft-github-operations[bot]"
<55726097+microsoft-github-operations[bot]@users.noreply.github.com>
Date: Wed, 4 Dec 2024 02:31:04 +0000
Subject: [PATCH 001/690] Initial commit
---
.gitignore | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 .gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..524f0963
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,24 @@
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+replay_pid*
From 2bcf3f5e23eff122d6b34d8825bcf5a86dc2a948 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Wed, 4 Dec 2024 15:31:11 +0800
Subject: [PATCH 002/690] build - Initial commit to setup Repo. (#4)
* Setup Checkstyle for the project.
* Add module Core, which stores code that is not related with ui.
* Add module UI, which stores code that is UI realted, for example SWT
related code.
---
.gitignore | 6 +
.project | 17 +
.settings/copilot4eclipse-cleanup-profile.xml | 14 +
.settings/dict | 2 +
.../github4eclipse-formatter-profile.xml | 164 +++++++
.settings/org.eclipse.m2e.core.prefs | 4 +
checkstyle.xml | 440 ++++++++++++++++++
.../.checkstyle | 7 +
com.microsoft.copilot.eclipse.core/.classpath | 7 +
com.microsoft.copilot.eclipse.core/.project | 40 ++
.../.settings/org.eclipse.jdt.core.prefs | 9 +
.../.settings/org.eclipse.m2e.core.prefs | 4 +
.../META-INF/MANIFEST.MF | 10 +
.../build.properties | 6 +
com.microsoft.copilot.eclipse.core/plugin.xml | 4 +
com.microsoft.copilot.eclipse.core/pom.xml | 28 ++
.../eclipse/core/CopilotCorePlugin.java | 21 +
com.microsoft.copilot.eclipse.ui/.checkstyle | 7 +
com.microsoft.copilot.eclipse.ui/.classpath | 7 +
com.microsoft.copilot.eclipse.ui/.project | 40 ++
.../.settings/org.eclipse.jdt.core.prefs | 9 +
.../.settings/org.eclipse.m2e.core.prefs | 4 +
.../META-INF/MANIFEST.MF | 11 +
.../build.properties | 5 +
com.microsoft.copilot.eclipse.ui/plugin.xml | 4 +
com.microsoft.copilot.eclipse.ui/pom.xml | 28 ++
.../copilot/eclipse/ui/CopilotUiPlugin.java | 21 +
pom.xml | 97 ++++
target-platform.target | 42 ++
29 files changed, 1058 insertions(+)
create mode 100644 .project
create mode 100644 .settings/copilot4eclipse-cleanup-profile.xml
create mode 100644 .settings/dict
create mode 100644 .settings/github4eclipse-formatter-profile.xml
create mode 100644 .settings/org.eclipse.m2e.core.prefs
create mode 100644 checkstyle.xml
create mode 100644 com.microsoft.copilot.eclipse.core/.checkstyle
create mode 100644 com.microsoft.copilot.eclipse.core/.classpath
create mode 100644 com.microsoft.copilot.eclipse.core/.project
create mode 100644 com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs
create mode 100644 com.microsoft.copilot.eclipse.core/.settings/org.eclipse.m2e.core.prefs
create mode 100644 com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
create mode 100644 com.microsoft.copilot.eclipse.core/build.properties
create mode 100644 com.microsoft.copilot.eclipse.core/plugin.xml
create mode 100644 com.microsoft.copilot.eclipse.core/pom.xml
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCorePlugin.java
create mode 100644 com.microsoft.copilot.eclipse.ui/.checkstyle
create mode 100644 com.microsoft.copilot.eclipse.ui/.classpath
create mode 100644 com.microsoft.copilot.eclipse.ui/.project
create mode 100644 com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
create mode 100644 com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.m2e.core.prefs
create mode 100644 com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
create mode 100644 com.microsoft.copilot.eclipse.ui/build.properties
create mode 100644 com.microsoft.copilot.eclipse.ui/plugin.xml
create mode 100644 com.microsoft.copilot.eclipse.ui/pom.xml
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUiPlugin.java
create mode 100644 pom.xml
create mode 100644 target-platform.target
diff --git a/.gitignore b/.gitignore
index 524f0963..6cb923f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,9 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
+
+# Mac files
+.DS_Store
+
+## Maven
+**/target/
diff --git a/.project b/.project
new file mode 100644
index 00000000..71c768cb
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ github-copilot-for-eclipse
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/.settings/copilot4eclipse-cleanup-profile.xml b/.settings/copilot4eclipse-cleanup-profile.xml
new file mode 100644
index 00000000..230109d1
--- /dev/null
+++ b/.settings/copilot4eclipse-cleanup-profile.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.settings/dict b/.settings/dict
new file mode 100644
index 00000000..dc6a5502
--- /dev/null
+++ b/.settings/dict
@@ -0,0 +1,2 @@
+copilot
+plugin
diff --git a/.settings/github4eclipse-formatter-profile.xml b/.settings/github4eclipse-formatter-profile.xml
new file mode 100644
index 00000000..b3e3d0e4
--- /dev/null
+++ b/.settings/github4eclipse-formatter-profile.xml
@@ -0,0 +1,164 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 00000000..f897a7f1
--- /dev/null
+++ b/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 00000000..24d9ebf3
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,440 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.core/.checkstyle b/com.microsoft.copilot.eclipse.core/.checkstyle
new file mode 100644
index 00000000..357a8477
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/.checkstyle
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.core/.classpath b/com.microsoft.copilot.eclipse.core/.classpath
new file mode 100644
index 00000000..8d861214
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.core/.project b/com.microsoft.copilot.eclipse.core/.project
new file mode 100644
index 00000000..957d06d4
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/.project
@@ -0,0 +1,40 @@
+
+
+ com.microsoft.copilot.eclipse.core
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+ net.sf.eclipsecs.core.CheckstyleBuilder
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+ net.sf.eclipsecs.core.CheckstyleNature
+
+
diff --git a/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs b/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000..62ef3488
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,9 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=17
diff --git a/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.m2e.core.prefs b/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 00000000..f897a7f1
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..e92232fc
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: com.microsoft.copilot.eclipse.core
+Bundle-SymbolicName: com.microsoft.copilot.eclipse.core;singleton:=true
+Bundle-Version: 0.1.0.qualifier
+Bundle-Activator: com.microsoft.copilot.eclipse.core.CopilotCorePlugin
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Automatic-Module-Name: com.microsoft.copilot.eclipse.core
+Bundle-ActivationPolicy: lazy
+Import-Package: org.osgi.framework;version="[1.10.0,2.0.0)"
diff --git a/com.microsoft.copilot.eclipse.core/build.properties b/com.microsoft.copilot.eclipse.core/build.properties
new file mode 100644
index 00000000..4c15b450
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/build.properties
@@ -0,0 +1,6 @@
+source.. = src
+output.. = target/classes
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
+
diff --git a/com.microsoft.copilot.eclipse.core/plugin.xml b/com.microsoft.copilot.eclipse.core/plugin.xml
new file mode 100644
index 00000000..f422d55d
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/plugin.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.core/pom.xml b/com.microsoft.copilot.eclipse.core/pom.xml
new file mode 100644
index 00000000..f6e55b27
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/pom.xml
@@ -0,0 +1,28 @@
+
+ 4.0.0
+
+ com.microsoft.copilot.eclipse
+ github-copilot-for-eclipse
+ 0.1.0-SNAPSHOT
+
+ com.microsoft.copilot.eclipse.core
+ eclipse-plugin
+ ${base.name} :: Core
+
+
+
+ org.eclipse.tycho
+ target-platform-configuration
+ ${tycho-version}
+
+
+ org.eclipse.tycho
+ tycho-maven-plugin
+ ${tycho-version}
+ true
+
+
+
+
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCorePlugin.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCorePlugin.java
new file mode 100644
index 00000000..c6ef7af5
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCorePlugin.java
@@ -0,0 +1,21 @@
+package com.microsoft.copilot.eclipse.core;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Activator class for the Copilot core plugin.
+ */
+public class CopilotCorePlugin implements BundleActivator {
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/.checkstyle b/com.microsoft.copilot.eclipse.ui/.checkstyle
new file mode 100644
index 00000000..357a8477
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/.checkstyle
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.ui/.classpath b/com.microsoft.copilot.eclipse.ui/.classpath
new file mode 100644
index 00000000..8d861214
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.ui/.project b/com.microsoft.copilot.eclipse.ui/.project
new file mode 100644
index 00000000..81edfba7
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/.project
@@ -0,0 +1,40 @@
+
+
+ com.microsoft.copilot.eclipse.ui
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+ net.sf.eclipsecs.core.CheckstyleBuilder
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+ net.sf.eclipsecs.core.CheckstyleNature
+
+
diff --git a/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs b/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000..62ef3488
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,9 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=17
diff --git a/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.m2e.core.prefs b/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 00000000..f897a7f1
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..b46c543d
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: com.microsoft.copilot.eclipse.ui
+Bundle-SymbolicName: com.microsoft.copilot.eclipse.ui;singleton:=true
+Bundle-Version: 0.1.0.qualifier
+Bundle-Activator: com.microsoft.copilot.eclipse.ui.CopilotUiPlugin
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Automatic-Module-Name: com.microsoft.copilot.eclipse.ui
+Bundle-ActivationPolicy: lazy
+Import-Package: org.osgi.framework;version="[1.10.0,2.0.0)"
+Require-Bundle: com.microsoft.copilot.eclipse.core;bundle-version="0.1.0"
diff --git a/com.microsoft.copilot.eclipse.ui/build.properties b/com.microsoft.copilot.eclipse.ui/build.properties
new file mode 100644
index 00000000..206f1966
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/build.properties
@@ -0,0 +1,5 @@
+source.. = src
+output.. = target/classes
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
diff --git a/com.microsoft.copilot.eclipse.ui/plugin.xml b/com.microsoft.copilot.eclipse.ui/plugin.xml
new file mode 100644
index 00000000..f422d55d
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/plugin.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.ui/pom.xml b/com.microsoft.copilot.eclipse.ui/pom.xml
new file mode 100644
index 00000000..8da60f2b
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/pom.xml
@@ -0,0 +1,28 @@
+
+ 4.0.0
+
+ com.microsoft.copilot.eclipse
+ github-copilot-for-eclipse
+ 0.1.0-SNAPSHOT
+
+ com.microsoft.copilot.eclipse.ui
+ eclipse-plugin
+ ${base.name} :: UI
+
+
+
+ org.eclipse.tycho
+ target-platform-configuration
+ ${tycho-version}
+
+
+ org.eclipse.tycho
+ tycho-maven-plugin
+ ${tycho-version}
+ true
+
+
+
+
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUiPlugin.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUiPlugin.java
new file mode 100644
index 00000000..9a0a59cd
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUiPlugin.java
@@ -0,0 +1,21 @@
+package com.microsoft.copilot.eclipse.ui;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Activator class for the Copilot UI plugin.
+ */
+public class CopilotUiPlugin implements BundleActivator {
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+
+ }
+
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 00000000..6a6090b3
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,97 @@
+
+
+ 4.0.0
+ com.microsoft.copilot.eclipse
+ github-copilot-for-eclipse
+ 0.1.0-SNAPSHOT
+ pom
+ ${base.name}
+
+
+ GitHub Copilot for Eclipse
+ 4.0.10
+ 3.6.0
+
+
+
+ com.microsoft.copilot.eclipse.core
+ com.microsoft.copilot.eclipse.ui
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ ${checkstyle-version}
+
+
+ com.puppycrawl.tools
+ checkstyle
+ 10.20.1
+
+
+
+ checkstyle.xml
+ UTF-8
+ true
+ true
+
+
+
+ verify
+
+ check
+
+
+
+
+
+ org.eclipse.tycho
+ tycho-maven-plugin
+ ${tycho-version}
+ true
+
+
+ org.eclipse.tycho
+ target-platform-configuration
+ ${tycho-version}
+
+
+ ../target-platform.target
+
+
+
+ macosx
+ cocoa
+ x86_64
+
+
+ macosx
+ cocoa
+ aarch64
+
+
+ linux
+ gtk
+ x86_64
+
+
+ linux
+ gtk
+ aarch64
+
+
+ win32
+ win32
+ x86_64
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/target-platform.target b/target-platform.target
new file mode 100644
index 00000000..e29da0bc
--- /dev/null
+++ b/target-platform.target
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.17.0
+ jar
+
+
+
+
+
+
+ io.reactivex.rxjava3
+ rxjava
+ 3.1.10
+
+
+
+
+
+
\ No newline at end of file
From 31e10ba11718dd822236e26b3fc47d31e72b152b Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Wed, 4 Dec 2024 16:03:20 +0800
Subject: [PATCH 003/690] build - Initialize CI pipeline (#5)
---
.azure-pipelines/ci.yml | 48 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 .azure-pipelines/ci.yml
diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml
new file mode 100644
index 00000000..2d2a1db9
--- /dev/null
+++ b/.azure-pipelines/ci.yml
@@ -0,0 +1,48 @@
+name: $(Date:yyyyMMdd).$(Rev:r)
+variables:
+ - name: Codeql.Enabled
+ value: true
+resources:
+ repositories:
+ - repository: self
+ type: git
+ ref: refs/heads/main
+ - repository: 1esPipelines
+ type: git
+ name: 1ESPipelineTemplates/1ESPipelineTemplates
+ ref: refs/tags/release
+trigger:
+ branches:
+ include:
+ - main
+extends:
+ template: v1/1ES.Unofficial.PipelineTemplate.yml@1esPipelines
+ parameters:
+ pool:
+ os: linux
+ name: 1ES_JavaTooling_Pool
+ image: 1ES_JavaTooling_Ubuntu-2004
+ sdl:
+ sourceAnalysisPool:
+ name: 1ES_JavaTooling_Pool
+ image: 1ES_JavaTooling_Windows_2022
+ os: windows
+ stages:
+ - stage: Build
+ jobs:
+ - job: Build
+ displayName: GitHub-Copilot-Eclipse-CI
+ steps:
+ - checkout: self
+ fetchTags: false
+ - task: JavaToolInstaller@0
+ displayName: Use Java 17
+ inputs:
+ versionSpec: "17"
+ jdkArchitectureOption: x64
+ jdkSourceOption: PreInstalled
+ - task: Maven@4
+ displayName: 'Run Maven Clean and Verify'
+ inputs:
+ goals: 'clean verify'
+ publishJUnitResults: false
\ No newline at end of file
From 912fc46edcf6801056732cb7471405f2f9860e50 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Thu, 5 Dec 2024 10:51:19 +0800
Subject: [PATCH 004/690] build - Add GitHub Actions for CI (#7)
---
.azure-pipelines/ci.yml | 48 -----
.github/workflows/ci.yml | 28 +++
.mvn/wrapper/maven-wrapper.properties | 19 ++
mvnw | 259 ++++++++++++++++++++++++++
mvnw.cmd | 149 +++++++++++++++
5 files changed, 455 insertions(+), 48 deletions(-)
delete mode 100644 .azure-pipelines/ci.yml
create mode 100644 .github/workflows/ci.yml
create mode 100644 .mvn/wrapper/maven-wrapper.properties
create mode 100755 mvnw
create mode 100644 mvnw.cmd
diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml
deleted file mode 100644
index 2d2a1db9..00000000
--- a/.azure-pipelines/ci.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-name: $(Date:yyyyMMdd).$(Rev:r)
-variables:
- - name: Codeql.Enabled
- value: true
-resources:
- repositories:
- - repository: self
- type: git
- ref: refs/heads/main
- - repository: 1esPipelines
- type: git
- name: 1ESPipelineTemplates/1ESPipelineTemplates
- ref: refs/tags/release
-trigger:
- branches:
- include:
- - main
-extends:
- template: v1/1ES.Unofficial.PipelineTemplate.yml@1esPipelines
- parameters:
- pool:
- os: linux
- name: 1ES_JavaTooling_Pool
- image: 1ES_JavaTooling_Ubuntu-2004
- sdl:
- sourceAnalysisPool:
- name: 1ES_JavaTooling_Pool
- image: 1ES_JavaTooling_Windows_2022
- os: windows
- stages:
- - stage: Build
- jobs:
- - job: Build
- displayName: GitHub-Copilot-Eclipse-CI
- steps:
- - checkout: self
- fetchTags: false
- - task: JavaToolInstaller@0
- displayName: Use Java 17
- inputs:
- versionSpec: "17"
- jdkArchitectureOption: x64
- jdkSourceOption: PreInstalled
- - task: Maven@4
- displayName: 'Run Maven Clean and Verify'
- inputs:
- goals: 'clean verify'
- publishJUnitResults: false
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..94386370
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,28 @@
+name: CI
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+ cache: maven
+ - name: Build with Maven
+ run: ./mvnw clean verify
+
+ # Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
+ - name: Update dependency graph
+ uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
+ continue-on-error: true
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..d58dfb70
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+wrapperVersion=3.3.2
+distributionType=only-script
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
diff --git a/mvnw b/mvnw
new file mode 100755
index 00000000..19529ddf
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,259 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.3.2
+#
+# Optional ENV vars
+# -----------------
+# JAVA_HOME - location of a JDK home dir, required when download maven via java source
+# MVNW_REPOURL - repo url base for downloading maven distribution
+# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
+# ----------------------------------------------------------------------------
+
+set -euf
+[ "${MVNW_VERBOSE-}" != debug ] || set -x
+
+# OS specific support.
+native_path() { printf %s\\n "$1"; }
+case "$(uname)" in
+CYGWIN* | MINGW*)
+ [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+ native_path() { cygpath --path --windows "$1"; }
+ ;;
+esac
+
+# set JAVACMD and JAVACCMD
+set_java_home() {
+ # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
+ if [ -n "${JAVA_HOME-}" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACCMD="$JAVA_HOME/jre/sh/javac"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ JAVACCMD="$JAVA_HOME/bin/javac"
+
+ if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
+ echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
+ echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
+ return 1
+ fi
+ fi
+ else
+ JAVACMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v java
+ )" || :
+ JAVACCMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v javac
+ )" || :
+
+ if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
+ echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
+ return 1
+ fi
+ fi
+}
+
+# hash string like Java String::hashCode
+hash_string() {
+ str="${1:-}" h=0
+ while [ -n "$str" ]; do
+ char="${str%"${str#?}"}"
+ h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
+ str="${str#?}"
+ done
+ printf %x\\n $h
+}
+
+verbose() { :; }
+[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
+
+die() {
+ printf %s\\n "$1" >&2
+ exit 1
+}
+
+trim() {
+ # MWRAPPER-139:
+ # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+ # Needed for removing poorly interpreted newline sequences when running in more
+ # exotic environments such as mingw bash on Windows.
+ printf "%s" "${1}" | tr -d '[:space:]'
+}
+
+# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
+while IFS="=" read -r key value; do
+ case "${key-}" in
+ distributionUrl) distributionUrl=$(trim "${value-}") ;;
+ distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
+ esac
+done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+
+case "${distributionUrl##*/}" in
+maven-mvnd-*bin.*)
+ MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
+ case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
+ *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
+ :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
+ :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
+ :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
+ *)
+ echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
+ distributionPlatform=linux-amd64
+ ;;
+ esac
+ distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
+ ;;
+maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
+*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+esac
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
+distributionUrlName="${distributionUrl##*/}"
+distributionUrlNameMain="${distributionUrlName%.*}"
+distributionUrlNameMain="${distributionUrlNameMain%-bin}"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
+
+exec_maven() {
+ unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
+ exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
+}
+
+if [ -d "$MAVEN_HOME" ]; then
+ verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ exec_maven "$@"
+fi
+
+case "${distributionUrl-}" in
+*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
+*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
+esac
+
+# prepare tmp dir
+if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
+ clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
+ trap clean HUP INT TERM EXIT
+else
+ die "cannot create temp dir"
+fi
+
+mkdir -p -- "${MAVEN_HOME%/*}"
+
+# Download and Install Apache Maven
+verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+verbose "Downloading from: $distributionUrl"
+verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+# select .zip or .tar.gz
+if ! command -v unzip >/dev/null; then
+ distributionUrl="${distributionUrl%.zip}.tar.gz"
+ distributionUrlName="${distributionUrl##*/}"
+fi
+
+# verbose opt
+__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
+[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
+
+# normalize http auth
+case "${MVNW_PASSWORD:+has-password}" in
+'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+esac
+
+if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
+ verbose "Found wget ... using wget"
+ wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
+elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
+ verbose "Found curl ... using curl"
+ curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
+elif set_java_home; then
+ verbose "Falling back to use Java to download"
+ javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
+ targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
+ cat >"$javaSource" <<-END
+ public class Downloader extends java.net.Authenticator
+ {
+ protected java.net.PasswordAuthentication getPasswordAuthentication()
+ {
+ return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
+ }
+ public static void main( String[] args ) throws Exception
+ {
+ setDefault( new Downloader() );
+ java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
+ }
+ }
+ END
+ # For Cygwin/MinGW, switch paths to Windows format before running javac and java
+ verbose " - Compiling Downloader.java ..."
+ "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
+ verbose " - Running Downloader.java ..."
+ "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
+fi
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+if [ -n "${distributionSha256Sum-}" ]; then
+ distributionSha256Result=false
+ if [ "$MVN_CMD" = mvnd.sh ]; then
+ echo "Checksum validation is not supported for maven-mvnd." >&2
+ echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ elif command -v sha256sum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ elif command -v shasum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+ echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ fi
+ if [ $distributionSha256Result = false ]; then
+ echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
+ echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+# unzip and move
+if command -v unzip >/dev/null; then
+ unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
+else
+ tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
+fi
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
+
+clean || :
+exec_maven "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 00000000..b150b91e
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,149 @@
+<# : batch portion
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.3.2
+@REM
+@REM Optional ENV vars
+@REM MVNW_REPOURL - repo url base for downloading maven distribution
+@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
+@REM ----------------------------------------------------------------------------
+
+@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
+@SET __MVNW_CMD__=
+@SET __MVNW_ERROR__=
+@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
+@SET PSModulePath=
+@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
+ IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
+)
+@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
+@SET __MVNW_PSMODULEP_SAVE=
+@SET __MVNW_ARG0_NAME__=
+@SET MVNW_USERNAME=
+@SET MVNW_PASSWORD=
+@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
+@echo Cannot start maven from wrapper >&2 && exit /b 1
+@GOTO :EOF
+: end batch / begin powershell #>
+
+$ErrorActionPreference = "Stop"
+if ($env:MVNW_VERBOSE -eq "true") {
+ $VerbosePreference = "Continue"
+}
+
+# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
+$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
+if (!$distributionUrl) {
+ Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+}
+
+switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
+ "maven-mvnd-*" {
+ $USE_MVND = $true
+ $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
+ $MVN_CMD = "mvnd.cmd"
+ break
+ }
+ default {
+ $USE_MVND = $false
+ $MVN_CMD = $script -replace '^mvnw','mvn'
+ break
+ }
+}
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+if ($env:MVNW_REPOURL) {
+ $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+ $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
+}
+$distributionUrlName = $distributionUrl -replace '^.*/',''
+$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
+$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
+if ($env:MAVEN_USER_HOME) {
+ $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
+}
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
+
+if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
+ Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+ exit $?
+}
+
+if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
+ Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
+}
+
+# prepare tmp dir
+$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
+$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
+$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
+trap {
+ if ($TMP_DOWNLOAD_DIR.Exists) {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+ }
+}
+
+New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
+
+# Download and Install Apache Maven
+Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+Write-Verbose "Downloading from: $distributionUrl"
+Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+$webclient = New-Object System.Net.WebClient
+if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
+ $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
+}
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
+if ($distributionSha256Sum) {
+ if ($USE_MVND) {
+ Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
+ }
+ Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
+ if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
+ Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
+ }
+}
+
+# unzip and move
+Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
+try {
+ Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
+} catch {
+ if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
+ Write-Error "fail to move MAVEN_HOME"
+ }
+} finally {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+}
+
+Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
From 7343af17069ab033c4ba2811bdb4070e37cf7b22 Mon Sep 17 00:00:00 2001
From: "microsoft-github-policy-service[bot]"
<77245923+microsoft-github-policy-service[bot]@users.noreply.github.com>
Date: Thu, 5 Dec 2024 10:52:28 +0800
Subject: [PATCH 005/690] eng - Microsoft mandatory file (#2)
Co-authored-by: microsoft-github-policy-service[bot] <77245923+microsoft-github-policy-service[bot]@users.noreply.github.com>
---
SECURITY.md | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 SECURITY.md
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..b3c89efc
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,41 @@
+
+
+## Security
+
+Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin).
+
+If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below.
+
+## Reporting Security Issues
+
+**Please do not report security vulnerabilities through public GitHub issues.**
+
+Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report).
+
+If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp).
+
+You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
+
+Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
+
+ * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
+ * Full paths of source file(s) related to the manifestation of the issue
+ * The location of the affected source code (tag/branch/commit or direct URL)
+ * Any special configuration required to reproduce the issue
+ * Step-by-step instructions to reproduce the issue
+ * Proof-of-concept or exploit code (if possible)
+ * Impact of the issue, including how an attacker might exploit the issue
+
+This information will help us triage your report more quickly.
+
+If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs.
+
+## Preferred Languages
+
+We prefer all communications to be in English.
+
+## Policy
+
+Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd).
+
+
From 5d25866875492420d71f860f3076276a76ce0b08 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Thu, 5 Dec 2024 10:57:19 +0800
Subject: [PATCH 006/690] eng - Apply format and cleanup profile to Eclipse
preference (#8)
---
... => copilot4eclipse-formatter-profile.xml} | 0
.../.settings/org.eclipse.jdt.core.prefs | 400 ++++++++++++++++++
.../.settings/org.eclipse.jdt.ui.prefs | 147 +++++++
.../.settings/org.eclipse.jdt.core.prefs | 400 ++++++++++++++++++
.../.settings/org.eclipse.jdt.ui.prefs | 147 +++++++
5 files changed, 1094 insertions(+)
rename .settings/{github4eclipse-formatter-profile.xml => copilot4eclipse-formatter-profile.xml} (100%)
create mode 100644 com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.ui.prefs
create mode 100644 com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.ui.prefs
diff --git a/.settings/github4eclipse-formatter-profile.xml b/.settings/copilot4eclipse-formatter-profile.xml
similarity index 100%
rename from .settings/github4eclipse-formatter-profile.xml
rename to .settings/copilot4eclipse-formatter-profile.xml
diff --git a/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs b/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs
index 62ef3488..c42a96bf 100644
--- a/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs
+++ b/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs
@@ -7,3 +7,403 @@ org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=17
+org.eclipse.jdt.core.formatter.align_arrows_in_switch_on_columns=false
+org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false
+org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
+org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line=false
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false
+org.eclipse.jdt.core.formatter.align_with_spaces=false
+org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assertion_message=0
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon=0
+org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_module_statements=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_record_components=16
+org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0
+org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow=0
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_type_annotations=0
+org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
+org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch=0
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case_after_arrow=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_record_constructor=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_record_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=false
+org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
+org.eclipse.jdt.core.formatter.comment.indent_tag_description=false
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags=do not insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.javadoc_do_not_separate_block_tags=false
+org.eclipse.jdt.core.formatter.comment.line_length=120
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=2
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_not_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert
+org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_line_comments=false
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.lineSplit=120
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.text_block_indentation=0
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
+org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true
+org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator=false
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
+org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
diff --git a/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.ui.prefs b/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 00000000..7f7518f0
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,147 @@
+cleanup.add_all=false
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_missing_override_annotations_interface_methods=true
+cleanup.add_serial_version_id=false
+cleanup.also_simplify_lambda=true
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.array_with_curly=false
+cleanup.arrays_fill=false
+cleanup.bitwise_conditional_expression=false
+cleanup.boolean_literal=false
+cleanup.boolean_value_rather_than_comparison=true
+cleanup.break_loop=false
+cleanup.collection_cloning=false
+cleanup.comparing_on_criteria=false
+cleanup.comparison_statement=false
+cleanup.controlflow_merge=false
+cleanup.convert_functional_interfaces=false
+cleanup.convert_to_enhanced_for_loop=true
+cleanup.convert_to_enhanced_for_loop_if_loop_var_used=true
+cleanup.convert_to_switch_expressions=false
+cleanup.correct_indentation=false
+cleanup.do_while_rather_than_while=true
+cleanup.double_negation=false
+cleanup.else_if=false
+cleanup.embedded_if=false
+cleanup.evaluate_nullable=false
+cleanup.extract_increment=false
+cleanup.format_source_code=true
+cleanup.format_source_code_changes_only=false
+cleanup.hash=false
+cleanup.if_condition=false
+cleanup.insert_inferred_type_arguments=false
+cleanup.instanceof=false
+cleanup.instanceof_keyword=false
+cleanup.invert_equals=false
+cleanup.join=false
+cleanup.lazy_logical_operator=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_type_abstract_if_missing_method=false
+cleanup.make_variable_declarations_final=false
+cleanup.map_cloning=false
+cleanup.merge_conditional_blocks=false
+cleanup.multi_catch=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.no_string_creation=false
+cleanup.no_super=false
+cleanup.number_suffix=false
+cleanup.objects_equals=false
+cleanup.one_if_rather_than_duplicate_blocks_that_fall_through=true
+cleanup.operand_factorization=false
+cleanup.organize_imports=true
+cleanup.overridden_assignment=false
+cleanup.overridden_assignment_move_decl=true
+cleanup.plain_replacement=false
+cleanup.precompile_regex=false
+cleanup.primitive_comparison=false
+cleanup.primitive_parsing=false
+cleanup.primitive_rather_than_wrapper=true
+cleanup.primitive_serialization=false
+cleanup.pull_out_if_from_if_else=false
+cleanup.pull_up_assignment=false
+cleanup.push_down_negation=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.reduce_indentation=false
+cleanup.redundant_comparator=false
+cleanup.redundant_falling_through_block_end=false
+cleanup.remove_private_constructors=true
+cleanup.remove_redundant_modifiers=false
+cleanup.remove_redundant_semicolons=true
+cleanup.remove_redundant_type_arguments=true
+cleanup.remove_trailing_whitespaces=true
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_array_creation=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_method_parameters=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.replace_deprecated_calls=false
+cleanup.return_expression=false
+cleanup.simplify_lambda_expression_and_method_ref=false
+cleanup.single_used_field=false
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.standard_comparison=false
+cleanup.static_inner_class=false
+cleanup.strictly_equal_or_different=false
+cleanup.stringbuffer_to_stringbuilder=false
+cleanup.stringbuilder=false
+cleanup.stringbuilder_for_local_vars=true
+cleanup.stringconcat_stringbuffer_stringbuilder=false
+cleanup.stringconcat_to_textblock=false
+cleanup.substring=false
+cleanup.switch=false
+cleanup.system_property=false
+cleanup.system_property_boolean=false
+cleanup.system_property_file_encoding=false
+cleanup.system_property_file_separator=false
+cleanup.system_property_line_separator=false
+cleanup.system_property_path_separator=false
+cleanup.ternary_operator=false
+cleanup.try_with_resource=false
+cleanup.unlooped_while=false
+cleanup.unreachable_block=false
+cleanup.use_anonymous_class_creation=false
+cleanup.use_autoboxing=false
+cleanup.use_blocks=true
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_directly_map_method=false
+cleanup.use_lambda=true
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_string_is_blank=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup.use_unboxing=false
+cleanup.use_var=false
+cleanup.useless_continue=false
+cleanup.useless_return=false
+cleanup.valueof_rather_than_instantiation=false
+cleanup_profile=_CheckStyle-Generated github-copilot-for-eclipse
+cleanup_settings_version=2
+eclipse.preferences.version=1
+formatter_profile=_CheckStyle-Generated github-copilot-for-eclipse
+formatter_settings_version=23
diff --git a/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs b/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
index 62ef3488..c42a96bf 100644
--- a/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
+++ b/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
@@ -7,3 +7,403 @@ org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=17
+org.eclipse.jdt.core.formatter.align_arrows_in_switch_on_columns=false
+org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false
+org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
+org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line=false
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false
+org.eclipse.jdt.core.formatter.align_with_spaces=false
+org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assertion_message=0
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon=0
+org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_module_statements=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_record_components=16
+org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0
+org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow=0
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_type_annotations=0
+org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
+org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch=0
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case_after_arrow=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_record_constructor=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_record_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=false
+org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
+org.eclipse.jdt.core.formatter.comment.indent_tag_description=false
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags=do not insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.javadoc_do_not_separate_block_tags=false
+org.eclipse.jdt.core.formatter.comment.line_length=120
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=2
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_not_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert
+org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_line_comments=false
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.lineSplit=120
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.text_block_indentation=0
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
+org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true
+org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator=false
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
+org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
diff --git a/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.ui.prefs b/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 00000000..7f7518f0
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,147 @@
+cleanup.add_all=false
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_missing_override_annotations_interface_methods=true
+cleanup.add_serial_version_id=false
+cleanup.also_simplify_lambda=true
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.array_with_curly=false
+cleanup.arrays_fill=false
+cleanup.bitwise_conditional_expression=false
+cleanup.boolean_literal=false
+cleanup.boolean_value_rather_than_comparison=true
+cleanup.break_loop=false
+cleanup.collection_cloning=false
+cleanup.comparing_on_criteria=false
+cleanup.comparison_statement=false
+cleanup.controlflow_merge=false
+cleanup.convert_functional_interfaces=false
+cleanup.convert_to_enhanced_for_loop=true
+cleanup.convert_to_enhanced_for_loop_if_loop_var_used=true
+cleanup.convert_to_switch_expressions=false
+cleanup.correct_indentation=false
+cleanup.do_while_rather_than_while=true
+cleanup.double_negation=false
+cleanup.else_if=false
+cleanup.embedded_if=false
+cleanup.evaluate_nullable=false
+cleanup.extract_increment=false
+cleanup.format_source_code=true
+cleanup.format_source_code_changes_only=false
+cleanup.hash=false
+cleanup.if_condition=false
+cleanup.insert_inferred_type_arguments=false
+cleanup.instanceof=false
+cleanup.instanceof_keyword=false
+cleanup.invert_equals=false
+cleanup.join=false
+cleanup.lazy_logical_operator=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_type_abstract_if_missing_method=false
+cleanup.make_variable_declarations_final=false
+cleanup.map_cloning=false
+cleanup.merge_conditional_blocks=false
+cleanup.multi_catch=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.no_string_creation=false
+cleanup.no_super=false
+cleanup.number_suffix=false
+cleanup.objects_equals=false
+cleanup.one_if_rather_than_duplicate_blocks_that_fall_through=true
+cleanup.operand_factorization=false
+cleanup.organize_imports=true
+cleanup.overridden_assignment=false
+cleanup.overridden_assignment_move_decl=true
+cleanup.plain_replacement=false
+cleanup.precompile_regex=false
+cleanup.primitive_comparison=false
+cleanup.primitive_parsing=false
+cleanup.primitive_rather_than_wrapper=true
+cleanup.primitive_serialization=false
+cleanup.pull_out_if_from_if_else=false
+cleanup.pull_up_assignment=false
+cleanup.push_down_negation=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.reduce_indentation=false
+cleanup.redundant_comparator=false
+cleanup.redundant_falling_through_block_end=false
+cleanup.remove_private_constructors=true
+cleanup.remove_redundant_modifiers=false
+cleanup.remove_redundant_semicolons=true
+cleanup.remove_redundant_type_arguments=true
+cleanup.remove_trailing_whitespaces=true
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_array_creation=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_method_parameters=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.replace_deprecated_calls=false
+cleanup.return_expression=false
+cleanup.simplify_lambda_expression_and_method_ref=false
+cleanup.single_used_field=false
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.standard_comparison=false
+cleanup.static_inner_class=false
+cleanup.strictly_equal_or_different=false
+cleanup.stringbuffer_to_stringbuilder=false
+cleanup.stringbuilder=false
+cleanup.stringbuilder_for_local_vars=true
+cleanup.stringconcat_stringbuffer_stringbuilder=false
+cleanup.stringconcat_to_textblock=false
+cleanup.substring=false
+cleanup.switch=false
+cleanup.system_property=false
+cleanup.system_property_boolean=false
+cleanup.system_property_file_encoding=false
+cleanup.system_property_file_separator=false
+cleanup.system_property_line_separator=false
+cleanup.system_property_path_separator=false
+cleanup.ternary_operator=false
+cleanup.try_with_resource=false
+cleanup.unlooped_while=false
+cleanup.unreachable_block=false
+cleanup.use_anonymous_class_creation=false
+cleanup.use_autoboxing=false
+cleanup.use_blocks=true
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_directly_map_method=false
+cleanup.use_lambda=true
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_string_is_blank=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup.use_unboxing=false
+cleanup.use_var=false
+cleanup.useless_continue=false
+cleanup.useless_return=false
+cleanup.valueof_rather_than_instantiation=false
+cleanup_profile=_CheckStyle-Generated github-copilot-for-eclipse
+cleanup_settings_version=2
+eclipse.preferences.version=1
+formatter_profile=_CheckStyle-Generated github-copilot-for-eclipse
+formatter_settings_version=23
From 26bbeee7c6a537c5f1bb543537c4cb4ee9f4d0aa Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Thu, 5 Dec 2024 11:29:21 +0800
Subject: [PATCH 007/690] build - Add Copilot agent (#9)
---
.github/workflows/ci.yml | 11 +++++++++
.gitignore | 6 ++++-
.../build.properties | 6 ++++-
.../copilot-agent/package-lock.json | 24 +++++++++++++++++++
.../copilot-agent/package.json | 17 +++++++++++++
5 files changed, 62 insertions(+), 2 deletions(-)
create mode 100644 com.microsoft.copilot.eclipse.core/copilot-agent/package-lock.json
create mode 100644 com.microsoft.copilot.eclipse.core/copilot-agent/package.json
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 94386370..125b4a99 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -13,12 +13,23 @@ jobs:
steps:
- uses: actions/checkout@v4
+
+ - name: Setup Node.js environment
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install Copilot agent
+ working-directory: com.microsoft.copilot.eclipse.core/copilot-agent
+ run: npm i
+
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: maven
+
- name: Build with Maven
run: ./mvnw clean verify
diff --git a/.gitignore b/.gitignore
index 6cb923f3..2e352862 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,5 +26,9 @@ replay_pid*
# Mac files
.DS_Store
-## Maven
+# Maven
**/target/
+
+# Copilot agent
+**/node_modules/
+**/copilot-agent/native/
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.core/build.properties b/com.microsoft.copilot.eclipse.core/build.properties
index 4c15b450..a19753f5 100644
--- a/com.microsoft.copilot.eclipse.core/build.properties
+++ b/com.microsoft.copilot.eclipse.core/build.properties
@@ -2,5 +2,9 @@ source.. = src
output.. = target/classes
bin.includes = META-INF/,\
.,\
- plugin.xml
+ plugin.xml,\
+ copilot-agent/native/darwin-arm64/,\
+ copilot-agent/native/darwin-x64/,\
+ copilot-agent/native/linux-x64/,\
+ copilot-agent/native/win32-x64/
diff --git a/com.microsoft.copilot.eclipse.core/copilot-agent/package-lock.json b/com.microsoft.copilot.eclipse.core/copilot-agent/package-lock.json
new file mode 100644
index 00000000..a2b5295b
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/copilot-agent/package-lock.json
@@ -0,0 +1,24 @@
+{
+ "name": "github-copilot-eclipse",
+ "version": "0.0.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "github-copilot-eclipse",
+ "version": "0.0.1",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@github/copilot-language-server": "^1.244.0"
+ }
+ },
+ "node_modules/@github/copilot-language-server": {
+ "version": "1.246.0",
+ "resolved": "https://registry.npmjs.org/@github/copilot-language-server/-/copilot-language-server-1.246.0.tgz",
+ "integrity": "sha512-THHDhaTs36DGYmi7OO2SZJke0AESBDxDjZYXFfMh+9TqIDAFkIaJiG4A4c6b5hjx3UzcV6pxe1LovKm9sKjJ8Q==",
+ "bin": {
+ "copilot-language-server": "dist/language-server.js"
+ }
+ }
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.core/copilot-agent/package.json b/com.microsoft.copilot.eclipse.core/copilot-agent/package.json
new file mode 100644
index 00000000..27e794ea
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/copilot-agent/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "github-copilot-eclipse",
+ "version": "0.0.1",
+ "description": "Sub package for managing @github/copilot-language-server dependency",
+ "publisher": "GitHub",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/microsoft/copilot-eclipse.git"
+ },
+ "scripts": {
+ "postinstall": "npx --yes copyfiles -u 3 'node_modules/@github/copilot-language-server/native/**/*' '.'"
+ },
+ "dependencies": {
+ "@github/copilot-language-server": "^1.244.0"
+ }
+}
From 255982c4493223de120cabcdf818c4b26fd3bf90 Mon Sep 17 00:00:00 2001
From: ethanyhou <149548697+ethanyhou@users.noreply.github.com>
Date: Fri, 6 Dec 2024 08:57:22 +0800
Subject: [PATCH 008/690] eng - Added launch configuration for project debug.
(#10)
---
launch/plugin_debug_configuration.launch | 192 +++++++++++++++++++++++
1 file changed, 192 insertions(+)
create mode 100644 launch/plugin_debug_configuration.launch
diff --git a/launch/plugin_debug_configuration.launch b/launch/plugin_debug_configuration.launch
new file mode 100644
index 00000000..fec96a04
--- /dev/null
+++ b/launch/plugin_debug_configuration.launch
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From edb3c37b9682223e779de16cc8b5d082066c077a Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Fri, 6 Dec 2024 13:31:11 +0800
Subject: [PATCH 009/690] feat - Initialize Copilot language server on plugin
activation (#11)
* Activate the GCLS(GitHub Copilot Language Server) when plugin is
activated.
* Check the local auth result from the GCLS and store the result to the
auth status manager.
---
.settings/dict | 1 +
checkstyle.xml | 3 +-
.../.classpath | 11 +++
.../.project | 34 +++++++
.../.settings/org.eclipse.jdt.core.prefs | 9 ++
.../.settings/org.eclipse.m2e.core.prefs | 4 +
.../META-INF/MANIFEST.MF | 14 +++
.../build.properties | 5 +
.../pom.xml | 17 ++++
.../eclipse/core/AuthStatusManagerTests.java | 41 ++++++++
.../lsp/LsStreamConnectionProviderTests.java | 32 ++++++
.../.settings/org.eclipse.jdt.core.prefs | 2 +-
.../META-INF/MANIFEST.MF | 11 ++-
com.microsoft.copilot.eclipse.core/plugin.xml | 24 +++++
.../eclipse/core/AuthStatusManager.java | 42 ++++++++
.../copilot/eclipse/core/CopilotCore.java | 84 ++++++++++++++++
.../eclipse/core/CopilotCorePlugin.java | 21 ----
.../core/lsp/CopilotLanguageClient.java | 11 +++
.../core/lsp/CopilotLanguageServer.java | 22 +++++
.../lsp/CopilotLanguageServerConnection.java | 44 +++++++++
.../core/lsp/LsStreamConnectionProvider.java | 99 +++++++++++++++++++
.../core/lsp/protocol/AuthStatusResult.java | 89 +++++++++++++++++
.../core/lsp/protocol/CheckStatusParams.java | 59 +++++++++++
.../lsp/protocol/CopilotCapabilities.java | 66 +++++++++++++
.../lsp/protocol/InitializationOptions.java | 93 +++++++++++++++++
.../core/lsp/protocol/NameAndVersion.java | 75 ++++++++++++++
.../eclipse/core/utils/PlatformUtils.java | 47 +++++++++
.../.settings/org.eclipse.jdt.core.prefs | 2 +-
pom.xml | 1 +
target-platform.target | 12 +++
30 files changed, 950 insertions(+), 25 deletions(-)
create mode 100644 com.microsoft.copilot.eclipse.core.test/.classpath
create mode 100644 com.microsoft.copilot.eclipse.core.test/.project
create mode 100644 com.microsoft.copilot.eclipse.core.test/.settings/org.eclipse.jdt.core.prefs
create mode 100644 com.microsoft.copilot.eclipse.core.test/.settings/org.eclipse.m2e.core.prefs
create mode 100644 com.microsoft.copilot.eclipse.core.test/META-INF/MANIFEST.MF
create mode 100644 com.microsoft.copilot.eclipse.core.test/build.properties
create mode 100644 com.microsoft.copilot.eclipse.core.test/pom.xml
create mode 100644 com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
create mode 100644 com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProviderTests.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/AuthStatusManager.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
delete mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCorePlugin.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageClient.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/AuthStatusResult.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CheckStatusParams.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CopilotCapabilities.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/InitializationOptions.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NameAndVersion.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/utils/PlatformUtils.java
diff --git a/.settings/dict b/.settings/dict
index dc6a5502..dd8d6872 100644
--- a/.settings/dict
+++ b/.settings/dict
@@ -1,2 +1,3 @@
copilot
plugin
+telemetry
diff --git a/checkstyle.xml b/checkstyle.xml
index 24d9ebf3..17ebfac9 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -313,7 +313,8 @@
-
+
+
diff --git a/com.microsoft.copilot.eclipse.core.test/.classpath b/com.microsoft.copilot.eclipse.core.test/.classpath
new file mode 100644
index 00000000..38f401ad
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core.test/.classpath
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.core.test/.project b/com.microsoft.copilot.eclipse.core.test/.project
new file mode 100644
index 00000000..4a7bc7b5
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core.test/.project
@@ -0,0 +1,34 @@
+
+
+ com.microsoft.copilot.eclipse.core.test
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/com.microsoft.copilot.eclipse.core.test/.settings/org.eclipse.jdt.core.prefs b/com.microsoft.copilot.eclipse.core.test/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000..62ef3488
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core.test/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,9 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=17
diff --git a/com.microsoft.copilot.eclipse.core.test/.settings/org.eclipse.m2e.core.prefs b/com.microsoft.copilot.eclipse.core.test/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 00000000..f897a7f1
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core.test/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/com.microsoft.copilot.eclipse.core.test/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.core.test/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..3443b426
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core.test/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: com.microsoft.copilot.eclipse.core.test
+Bundle-SymbolicName: com.microsoft.copilot.eclipse.core.test;singleton:=true
+Bundle-Version: 0.1.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Automatic-Module-Name: com.microsoft.copilot.eclipse.core.test
+Import-Package: org.objenesis;version="[3.4.0,4.0.0)",
+ org.osgi.framework;version="[1.10.0,2.0.0)"
+Require-Bundle: com.microsoft.copilot.eclipse.core;bundle-version="0.1.0",
+ org.mockito.mockito-core;bundle-version="5.14.2",
+ org.junit;bundle-version="4.13.2",
+ org.eclipse.lsp4e;bundle-version="0.18.12",
+ org.eclipse.jdt.annotation;bundle-version="2.3.0"
diff --git a/com.microsoft.copilot.eclipse.core.test/build.properties b/com.microsoft.copilot.eclipse.core.test/build.properties
new file mode 100644
index 00000000..100b378a
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core.test/build.properties
@@ -0,0 +1,5 @@
+source.. = src
+output.. = target/classes
+bin.includes = META-INF/,\
+ .
+
diff --git a/com.microsoft.copilot.eclipse.core.test/pom.xml b/com.microsoft.copilot.eclipse.core.test/pom.xml
new file mode 100644
index 00000000..3f28a5d2
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core.test/pom.xml
@@ -0,0 +1,17 @@
+
+ 4.0.0
+
+ com.microsoft.copilot.eclipse
+ github-copilot-for-eclipse
+ 0.1.0-SNAPSHOT
+
+ com.microsoft.copilot.eclipse.core.test
+ eclipse-test-plugin
+ ${base.name} :: Core Tests
+
+
+ true
+
+
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
new file mode 100644
index 00000000..22663f06
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
@@ -0,0 +1,41 @@
+package com.microsoft.copilot.eclipse.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import java.util.concurrent.CompletableFuture;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AuthStatusManagerTests {
+
+ @Mock
+ CopilotLanguageServerConnection mockConnection;
+ AuthStatusManager authStatusManager;
+
+ @Before
+ public void setUp() {
+ authStatusManager = new AuthStatusManager(mockConnection);
+ }
+
+ @Test
+ public void testAuthStatusResultOnSuccess() {
+ AuthStatusResult expectedResult = new AuthStatusResult();
+ expectedResult.setStatus(AuthStatusResult.OK);
+ when(mockConnection.checkStatus(false)).thenReturn(CompletableFuture.completedFuture(expectedResult));
+
+ authStatusManager.checkStatus();
+
+ assertEquals(AuthStatusResult.OK, authStatusManager.getAuthStatusResult().getStatus());
+
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProviderTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProviderTests.java
new file mode 100644
index 00000000..1b7e96bd
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProviderTests.java
@@ -0,0 +1,32 @@
+package com.microsoft.copilot.eclipse.core.lsp;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import com.microsoft.copilot.eclipse.core.lsp.protocol.InitializationOptions;
+
+public class LsStreamConnectionProviderTests {
+
+ @Test
+ public void testInitializationOptions() {
+ LsStreamConnectionProvider provider = new LsStreamConnectionProvider();
+
+ InitializationOptions options = (InitializationOptions) provider.getInitializationOptions(null);
+
+ assertEquals(LsStreamConnectionProvider.EDITOR_NAME, options.getEditorInfo().getName());
+ assertEquals(LsStreamConnectionProvider.EDITOR_PLUGIN_NAME, options.getEditorPluginInfo().getName());
+ }
+
+ @Test
+ public void testStartLanguageServer() throws IOException {
+ LsStreamConnectionProvider provider = new LsStreamConnectionProvider();
+ try {
+ provider.start();
+ } finally {
+ provider.stop();
+ }
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs b/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs
index c42a96bf..aa5854b6 100644
--- a/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs
+++ b/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs
@@ -150,7 +150,7 @@ org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
diff --git a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
index e92232fc..ea216d43 100644
--- a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
@@ -3,8 +3,17 @@ Bundle-ManifestVersion: 2
Bundle-Name: com.microsoft.copilot.eclipse.core
Bundle-SymbolicName: com.microsoft.copilot.eclipse.core;singleton:=true
Bundle-Version: 0.1.0.qualifier
-Bundle-Activator: com.microsoft.copilot.eclipse.core.CopilotCorePlugin
+Export-Package: com.microsoft.copilot.eclipse.core,
+ com.microsoft.copilot.eclipse.core.lsp,
+ com.microsoft.copilot.eclipse.core.lsp.protocol
+Bundle-Activator: com.microsoft.copilot.eclipse.core.CopilotCore
Bundle-RequiredExecutionEnvironment: JavaSE-17
Automatic-Module-Name: com.microsoft.copilot.eclipse.core
Bundle-ActivationPolicy: lazy
Import-Package: org.osgi.framework;version="[1.10.0,2.0.0)"
+Require-Bundle: org.eclipse.lsp4e;bundle-version="0.18.12",
+ org.eclipse.core.runtime;bundle-version="3.31.100",
+ org.eclipse.lsp4j;bundle-version="0.23.1",
+ org.eclipse.lsp4j.jsonrpc;bundle-version="0.23.1",
+ org.apache.commons.lang3;bundle-version="3.17.0",
+ org.eclipse.jdt.annotation;bundle-version="2.3.0"
diff --git a/com.microsoft.copilot.eclipse.core/plugin.xml b/com.microsoft.copilot.eclipse.core/plugin.xml
index f422d55d..a55a9fda 100644
--- a/com.microsoft.copilot.eclipse.core/plugin.xml
+++ b/com.microsoft.copilot.eclipse.core/plugin.xml
@@ -1,4 +1,28 @@
+
+
+
+
+
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/AuthStatusManager.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/AuthStatusManager.java
new file mode 100644
index 00000000..22cb4e6e
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/AuthStatusManager.java
@@ -0,0 +1,42 @@
+package com.microsoft.copilot.eclipse.core;
+
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
+
+/**
+ * Manager for the authentication status.
+ */
+public class AuthStatusManager {
+
+ private CopilotLanguageServerConnection connection;
+
+ private AuthStatusResult authStatusResult;
+
+ /**
+ * Constructor for the AuthStatusManager.
+ *
+ * @param connection the connection to the language server.
+ */
+ public AuthStatusManager(CopilotLanguageServerConnection connection) {
+ this.connection = connection;
+ this.authStatusResult = new AuthStatusResult();
+ this.authStatusResult.setStatus(AuthStatusResult.NOT_SIGNED_IN);
+ }
+
+ /**
+ * Check the login status for current machine.
+ */
+ public void checkStatus() {
+ this.connection.checkStatus(false).thenAccept(result -> {
+ this.authStatusResult = result;
+ }).exceptionally(ex -> {
+ // TODO: log & send telemetry
+ this.authStatusResult.setStatus(AuthStatusResult.ERROR);
+ return null;
+ });
+ }
+
+ public AuthStatusResult getAuthStatusResult() {
+ return authStatusResult;
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
new file mode 100644
index 00000000..0dd2cd8d
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
@@ -0,0 +1,84 @@
+package com.microsoft.copilot.eclipse.core;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.lsp4e.LanguageServerWrapper;
+import org.eclipse.lsp4e.LanguageServersRegistry;
+import org.eclipse.lsp4e.LanguageServiceAccessor;
+import org.osgi.framework.BundleContext;
+
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+
+/**
+ * Activator class for the Copilot core plugin.
+ */
+public class CopilotCore extends Plugin {
+
+ private CopilotLanguageServerConnection copilotLanguageServer;
+ private AuthStatusManager authStatusManager;
+
+ private static CopilotCore COPILOT_CORE_PLUGIN = null;
+
+ /**
+ * Creates the Copilot core plugin. The plugin is created automatically by the Eclipse framework. Clients must not
+ * call this constructor.
+ */
+ public CopilotCore() {
+ super();
+ COPILOT_CORE_PLUGIN = this;
+ }
+
+ public static Plugin getPlugin() {
+ return COPILOT_CORE_PLUGIN;
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ init();
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+
+ }
+
+ @SuppressWarnings("restriction")
+ void init() {
+ final Runnable initRunnable = () -> {
+ LanguageServersRegistry.LanguageServerDefinition serverDef = LanguageServersRegistry.getInstance()
+ .getDefinition(CopilotLanguageServerConnection.SERVER_ID);
+ if (serverDef == null) {
+ // TODO: log & send telemetry
+ throw new IllegalStateException(
+ "Language server definition not found for " + CopilotLanguageServerConnection.SERVER_ID);
+ }
+
+ LanguageServerWrapper wrapper = LanguageServiceAccessor.startLanguageServer(serverDef);
+ this.copilotLanguageServer = new CopilotLanguageServerConnection(wrapper);
+ this.authStatusManager = new AuthStatusManager(this.copilotLanguageServer);
+
+ this.authStatusManager.checkStatus();
+ };
+
+ Job initJob = new Job("GitHub Copilot Initialization...") {
+ protected IStatus run(IProgressMonitor monitor) {
+ initRunnable.run();
+ return Status.OK_STATUS;
+ }
+ };
+ initJob.setUser(true);
+ initJob.schedule();
+ }
+
+ public CopilotLanguageServerConnection getCopilotLanguageServer() {
+ return copilotLanguageServer;
+ }
+
+ public AuthStatusManager getAuthStatusManager() {
+ return authStatusManager;
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCorePlugin.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCorePlugin.java
deleted file mode 100644
index c6ef7af5..00000000
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCorePlugin.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.microsoft.copilot.eclipse.core;
-
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-
-/**
- * Activator class for the Copilot core plugin.
- */
-public class CopilotCorePlugin implements BundleActivator {
-
- @Override
- public void start(BundleContext context) throws Exception {
-
- }
-
- @Override
- public void stop(BundleContext context) throws Exception {
-
- }
-
-}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageClient.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageClient.java
new file mode 100644
index 00000000..699fff08
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageClient.java
@@ -0,0 +1,11 @@
+package com.microsoft.copilot.eclipse.core.lsp;
+
+import org.eclipse.lsp4e.LanguageClientImpl;
+
+/**
+ * Language client for the Copilot language server.
+ */
+@SuppressWarnings("restriction")
+public class CopilotLanguageClient extends LanguageClientImpl {
+
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java
new file mode 100644
index 00000000..1945d79f
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java
@@ -0,0 +1,22 @@
+package com.microsoft.copilot.eclipse.core.lsp;
+
+import java.util.concurrent.CompletableFuture;
+
+import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
+import org.eclipse.lsp4j.services.LanguageServer;
+
+import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CheckStatusParams;
+
+/**
+ * Interface for Copilot Language Server.
+ */
+public interface CopilotLanguageServer extends LanguageServer {
+
+ /**
+ * Check the login status for current machine.
+ */
+ @JsonRequest
+ CompletableFuture checkStatus(CheckStatusParams param);
+
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
new file mode 100644
index 00000000..eab1a722
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
@@ -0,0 +1,44 @@
+package com.microsoft.copilot.eclipse.core.lsp;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+
+import org.eclipse.lsp4e.LanguageServerWrapper;
+import org.eclipse.lsp4j.services.LanguageServer;
+
+import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CheckStatusParams;
+
+/**
+ * Language Server for Copilot agent.
+ */
+@SuppressWarnings("restriction")
+public class CopilotLanguageServerConnection {
+
+ public static final String SERVER_ID = "com.microsoft.copilot.eclipse.ls";
+
+ private LanguageServerWrapper languageServerWrapper;
+
+ /**
+ * Constructor for the CopilotLanguageServer.
+ *
+ * @param languageServerWrapper the language server wrapper.
+ */
+ public CopilotLanguageServerConnection(LanguageServerWrapper languageServerWrapper) {
+ this.languageServerWrapper = languageServerWrapper;
+ }
+
+ /**
+ * Check the login status for current machine.
+ */
+ @SuppressWarnings("null")
+ public CompletableFuture checkStatus(Boolean localCheckOnly) {
+ Function> fn = server -> {
+ CheckStatusParams param = new CheckStatusParams();
+ param.setLocalChecksOnly(localCheckOnly);
+ return ((CopilotLanguageServer) server).checkStatus(param);
+ };
+ return this.languageServerWrapper.execute(fn);
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
new file mode 100644
index 00000000..4c3daf39
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
@@ -0,0 +1,99 @@
+package com.microsoft.copilot.eclipse.core.lsp;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider;
+import org.osgi.framework.Bundle;
+
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotCapabilities;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.InitializationOptions;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.NameAndVersion;
+import com.microsoft.copilot.eclipse.core.utils.PlatformUtils;
+
+/**
+ * Stream connection provider for the Copilot language server.
+ */
+public class LsStreamConnectionProvider extends ProcessStreamConnectionProvider {
+
+ public static final String EDITOR_NAME = "Eclipse";
+
+ public static final String EDITOR_PLUGIN_NAME = "GotHub Copilot for Eclipse";
+
+ @Override
+ public Object getInitializationOptions(@Nullable URI rootUri) {
+ NameAndVersion editorInfo = new NameAndVersion(EDITOR_NAME, PlatformUtils.getEclipseVersion());
+ Bundle bundle = CopilotCore.getPlugin().getBundle();
+ String bundleVersion = bundle == null ? "unknown" : bundle.getVersion().toString();
+ NameAndVersion editorPluginInfo = new NameAndVersion(EDITOR_PLUGIN_NAME, bundleVersion);
+ CopilotCapabilities capabilities = new CopilotCapabilities(false, false);
+ return new InitializationOptions(editorInfo, editorPluginInfo, capabilities);
+ }
+
+ @Override
+ public void start() throws IOException {
+ Path binary = findBinary();
+ if (binary == null) {
+ throw new IOException("Could not find the language server binary");
+ }
+
+ File executable = binary.toFile();
+ if (!executable.canExecute()) {
+ boolean canExecute = executable.setExecutable(true);
+ if (!canExecute) {
+ // TODO: throw error or handle it?
+ }
+ }
+ List commands = new LinkedList<>();
+ commands.add(binary.toString());
+ commands.add("--stdio");
+ this.setCommands(commands);
+ // TODO: will it have permission issue on Unix based OS?
+ super.start();
+ }
+
+ private @Nullable Path findBinary() throws IOException {
+ if (PlatformUtils.isMac() && PlatformUtils.isIntel64()) {
+ return null;
+ }
+
+ Path binDir = findAgentBinaryDirectoryPath();
+ if (binDir == null) {
+ return null;
+ }
+
+ Path executable = null;
+ if (PlatformUtils.isLinux()) {
+ executable = binDir.resolve("linux-x64/copilot-language-server");
+ } else if (PlatformUtils.isWindows()) {
+ executable = binDir.resolve("win32-x64/copilot-language-server.exe");
+ } else if (PlatformUtils.isMac()) {
+ if (PlatformUtils.isArm64()) {
+ executable = binDir.resolve("darwin-arm64/copilot-language-server");
+ } else {
+ executable = binDir.resolve("darwin-x64/copilot-language-server");
+ }
+ }
+
+ return executable != null && Files.exists(executable) ? executable : null;
+ }
+
+ private @Nullable Path findAgentBinaryDirectoryPath() throws IOException {
+ URL url = CopilotCore.getPlugin().getBundle().getEntry("copilot-agent/native");
+ if (url == null) {
+ return null;
+ }
+
+ return Path.of(FileLocator.toFileURL(url).getPath());
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/AuthStatusResult.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/AuthStatusResult.java
new file mode 100644
index 00000000..37c95e28
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/AuthStatusResult.java
@@ -0,0 +1,89 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Objects;
+
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * Result for the Authentication status.
+ */
+public class AuthStatusResult {
+
+ public static final String OK = "OK";
+ public static final String ERROR = "Error";
+ public static final String WARNING = "Warning";
+ public static final String NOT_SIGNED_IN = "NotSignedIn";
+ public static final String NOT_AUTHORIZED = "NotAuthorized";
+
+ @NonNull
+ private String status;
+
+ private String user;
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public boolean isSignedIn() {
+ return OK.equals(this.status) && StringUtils.isNotEmpty(this.user);
+ }
+
+ public boolean isNotSignedIn() {
+ return NOT_SIGNED_IN.equals(this.status);
+ }
+
+ public boolean isWarning() {
+ return WARNING.equals(this.status);
+ }
+
+ public boolean isError() {
+ return ERROR.equals(this.status);
+ }
+
+ public boolean isNotAuthorized() {
+ return NOT_AUTHORIZED.equals(this.status);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(status, user);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ AuthStatusResult other = (AuthStatusResult) obj;
+ return Objects.equals(status, other.status) && Objects.equals(user, other.user);
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("status", status);
+ builder.add("user", user);
+ return builder.toString();
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CheckStatusParams.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CheckStatusParams.java
new file mode 100644
index 00000000..43b11091
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CheckStatusParams.java
@@ -0,0 +1,59 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Objects;
+
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+
+/**
+ * Parameter used for the checkStatus request.
+ */
+public class CheckStatusParams {
+
+ private boolean localChecksOnly;
+
+ private boolean forceRefresh;
+
+ public boolean isLocalChecksOnly() {
+ return localChecksOnly;
+ }
+
+ public void setLocalChecksOnly(boolean localChecksOnly) {
+ this.localChecksOnly = localChecksOnly;
+ }
+
+ public boolean isForceRefresh() {
+ return forceRefresh;
+ }
+
+ public void setForceRefresh(boolean forceRefresh) {
+ this.forceRefresh = forceRefresh;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(forceRefresh, localChecksOnly);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ CheckStatusParams other = (CheckStatusParams) obj;
+ return forceRefresh == other.forceRefresh && localChecksOnly == other.localChecksOnly;
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("localChecksOnly", localChecksOnly);
+ builder.add("forceRefresh", forceRefresh);
+ return builder.toString();
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CopilotCapabilities.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CopilotCapabilities.java
new file mode 100644
index 00000000..ad4b32c7
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CopilotCapabilities.java
@@ -0,0 +1,66 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Objects;
+
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+
+/**
+ * Capabilities of the Copilot language server.
+ */
+public class CopilotCapabilities {
+ private boolean fetch;
+
+ private boolean watchedFiles;
+
+ /**
+ * Creates a new CopilotCapabilities.
+ */
+ public CopilotCapabilities(boolean fetch, boolean watchedFiles) {
+ this.fetch = fetch;
+ this.watchedFiles = watchedFiles;
+ }
+
+ public boolean isFetch() {
+ return fetch;
+ }
+
+ public void setFetch(boolean fetch) {
+ this.fetch = fetch;
+ }
+
+ public boolean isWatchedFiles() {
+ return watchedFiles;
+ }
+
+ public void setWatchedFiles(boolean watchedFiles) {
+ this.watchedFiles = watchedFiles;
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("fetch", fetch);
+ builder.add("watchedFiles", watchedFiles);
+ return builder.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(fetch, watchedFiles);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ CopilotCapabilities other = (CopilotCapabilities) obj;
+ return fetch == other.fetch && watchedFiles == other.watchedFiles;
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/InitializationOptions.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/InitializationOptions.java
new file mode 100644
index 00000000..4e1ab16c
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/InitializationOptions.java
@@ -0,0 +1,93 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Objects;
+
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * Initialization options for the Copilot language server.
+ */
+public class InitializationOptions {
+ @NonNull
+ private NameAndVersion editorInfo;
+
+ @NonNull
+ private NameAndVersion editorPluginInfo;
+
+ private CopilotCapabilities capabilities;
+
+ /**
+ * Creates a new InitializationOptions.
+ */
+ public InitializationOptions(NameAndVersion editorInfo, NameAndVersion editorPluginInfo) {
+ this.editorInfo = editorInfo;
+ this.editorPluginInfo = editorPluginInfo;
+ }
+
+ /**
+ * Creates a new InitializationOptions.
+ */
+ public InitializationOptions(NameAndVersion editorInfo, NameAndVersion editorPluginInfo,
+ CopilotCapabilities capabilities) {
+ this.editorInfo = editorInfo;
+ this.editorPluginInfo = editorPluginInfo;
+ this.capabilities = capabilities;
+ }
+
+ @NonNull
+ public NameAndVersion getEditorInfo() {
+ return editorInfo;
+ }
+
+ public void setEditorInfo(@NonNull NameAndVersion editorInfo) {
+ this.editorInfo = editorInfo;
+ }
+
+ @NonNull
+ public NameAndVersion getEditorPluginInfo() {
+ return editorPluginInfo;
+ }
+
+ public void setEditorPluginInfo(@NonNull NameAndVersion editorPluginInfo) {
+ this.editorPluginInfo = editorPluginInfo;
+ }
+
+ public CopilotCapabilities getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(CopilotCapabilities capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("editorInfo", editorInfo);
+ builder.add("editorPluginInfo", editorPluginInfo);
+ builder.add("capabilities", capabilities);
+ return builder.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(capabilities, editorInfo, editorPluginInfo);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ InitializationOptions other = (InitializationOptions) obj;
+ return Objects.equals(capabilities, other.capabilities) && Objects.equals(editorInfo, other.editorInfo)
+ && Objects.equals(editorPluginInfo, other.editorPluginInfo);
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NameAndVersion.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NameAndVersion.java
new file mode 100644
index 00000000..b0a0d9c0
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NameAndVersion.java
@@ -0,0 +1,75 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Objects;
+
+import org.eclipse.lsp4j.jsonrpc.util.Preconditions;
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * A name and version pair.
+ */
+public class NameAndVersion {
+ @NonNull
+ private String name;
+
+ @NonNull
+ private String version;
+
+ /**
+ * Creates a new NameAndVersion.
+ *
+ * @param name the name.
+ * @param version the version.
+ */
+ public NameAndVersion(@NonNull String name, @NonNull String version) {
+ this.name = Preconditions.checkNotNull(name, "name");
+ this.version = Preconditions.checkNotNull(version, "version");
+ }
+
+ @NonNull
+ public String getName() {
+ return name;
+ }
+
+ public void setName(@NonNull String name) {
+ this.name = name;
+ }
+
+ @NonNull
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(@NonNull String version) {
+ this.version = version;
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder b = new ToStringBuilder(this);
+ b.add("name", name);
+ b.add("version", version);
+ return b.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, version);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ NameAndVersion other = (NameAndVersion) obj;
+ return Objects.equals(name, other.name) && Objects.equals(version, other.version);
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/utils/PlatformUtils.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/utils/PlatformUtils.java
new file mode 100644
index 00000000..9168986e
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/utils/PlatformUtils.java
@@ -0,0 +1,47 @@
+package com.microsoft.copilot.eclipse.core.utils;
+
+import org.eclipse.core.runtime.Platform;
+import org.osgi.framework.Bundle;
+
+/**
+ * Utility class for platform related operations.
+ */
+public class PlatformUtils {
+
+ public static final String EC_PLATFORM_BUNDLE_NAME = "org.eclipse.platform";
+
+ private PlatformUtils() {
+ }
+
+ /**
+ * Get the version of the Eclipse platform.
+ */
+ public static String getEclipseVersion() {
+ Bundle bundle = Platform.getBundle(EC_PLATFORM_BUNDLE_NAME);
+ if (bundle == null) {
+ return "unknown";
+ }
+ return bundle.getVersion().toString();
+ }
+
+ public static boolean isMac() {
+ return Platform.getOS().equals(Platform.OS_MACOSX);
+ }
+
+ public static boolean isLinux() {
+ return Platform.getOS().equals(Platform.OS_LINUX);
+ }
+
+ public static boolean isWindows() {
+ return Platform.getOS().equals(Platform.OS_WIN32);
+ }
+
+ public static boolean isIntel64() {
+ return Platform.getOSArch().equals(Platform.ARCH_X86_64);
+ }
+
+ public static boolean isArm64() {
+ return Platform.getOSArch().equals(Platform.ARCH_AARCH64);
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs b/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
index c42a96bf..aa5854b6 100644
--- a/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
+++ b/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
@@ -150,7 +150,7 @@ org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
diff --git a/pom.xml b/pom.xml
index 6a6090b3..90293ef3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,6 +18,7 @@
com.microsoft.copilot.eclipse.core
+ com.microsoft.copilot.eclipse.core.test
com.microsoft.copilot.eclipse.ui
diff --git a/target-platform.target b/target-platform.target
index e29da0bc..8fac5a31 100644
--- a/target-platform.target
+++ b/target-platform.target
@@ -10,6 +10,8 @@
+
+
@@ -34,6 +36,16 @@
rxjava
3.1.10
+
+ org.mockito
+ mockito-core
+ 5.14.2
+
+
+ org.objenesis
+ objenesis
+ 3.4
+
From da57b7ca2d091cd6cad43a939be05f0579643010 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Fri, 6 Dec 2024 15:20:53 +0800
Subject: [PATCH 010/690] eng - Update issue templates and code owners (#12)
---
.github/CODEOWNERS | 1 +
.github/ISSUE_TEMPLATE/bug_report.md | 32 +++++++++++++++++++++++
.github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++++
3 files changed, 53 insertions(+)
create mode 100644 .github/CODEOWNERS
create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md
create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000..67e6be67
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @jdneo @ethanyhou @yanshudan
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..28db62a1
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,32 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: "[Bug] "
+labels: bug
+assignees: ''
+
+---
+
+**Environment**
+- OS:
+- Eclipse Version:
+- Plugin Version:
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..54c28deb
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: "[Feat] "
+labels: enhancement
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
From 669e5cc59fc489c3c162028258df05db890bbd0d Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Fri, 6 Dec 2024 16:03:27 +0800
Subject: [PATCH 011/690] test - Migrate test framework to JUnit 5 (#13)
---
.../META-INF/MANIFEST.MF | 5 +++--
.../eclipse/core/AuthStatusManagerTests.java | 18 +++++++++---------
.../lsp/LsStreamConnectionProviderTests.java | 10 +++++-----
.../copilot/eclipse/core/CopilotCore.java | 4 +++-
.../lsp/CopilotLanguageServerConnection.java | 7 +++++++
target-platform.target | 8 +++++++-
6 files changed, 34 insertions(+), 18 deletions(-)
diff --git a/com.microsoft.copilot.eclipse.core.test/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.core.test/META-INF/MANIFEST.MF
index 3443b426..2112db88 100644
--- a/com.microsoft.copilot.eclipse.core.test/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.core.test/META-INF/MANIFEST.MF
@@ -9,6 +9,7 @@ Import-Package: org.objenesis;version="[3.4.0,4.0.0)",
org.osgi.framework;version="[1.10.0,2.0.0)"
Require-Bundle: com.microsoft.copilot.eclipse.core;bundle-version="0.1.0",
org.mockito.mockito-core;bundle-version="5.14.2",
- org.junit;bundle-version="4.13.2",
org.eclipse.lsp4e;bundle-version="0.18.12",
- org.eclipse.jdt.annotation;bundle-version="2.3.0"
+ org.eclipse.jdt.annotation;bundle-version="2.3.0",
+ junit-jupiter-api;bundle-version="5.11.0",
+ org.mockito.junit-jupiter;bundle-version="5.14.2"
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
index 22663f06..596e8416 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
@@ -1,33 +1,33 @@
package com.microsoft.copilot.eclipse.core;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import java.util.concurrent.CompletableFuture;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.jupiter.MockitoExtension;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
-@RunWith(MockitoJUnitRunner.class)
-public class AuthStatusManagerTests {
+@ExtendWith(MockitoExtension.class)
+class AuthStatusManagerTests {
@Mock
CopilotLanguageServerConnection mockConnection;
AuthStatusManager authStatusManager;
- @Before
+ @BeforeEach
public void setUp() {
authStatusManager = new AuthStatusManager(mockConnection);
}
@Test
- public void testAuthStatusResultOnSuccess() {
+ void testAuthStatusResultOnSuccess() {
AuthStatusResult expectedResult = new AuthStatusResult();
expectedResult.setStatus(AuthStatusResult.OK);
when(mockConnection.checkStatus(false)).thenReturn(CompletableFuture.completedFuture(expectedResult));
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProviderTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProviderTests.java
index 1b7e96bd..f866d4ea 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProviderTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProviderTests.java
@@ -1,17 +1,17 @@
package com.microsoft.copilot.eclipse.core.lsp;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import com.microsoft.copilot.eclipse.core.lsp.protocol.InitializationOptions;
-public class LsStreamConnectionProviderTests {
+class LsStreamConnectionProviderTests {
@Test
- public void testInitializationOptions() {
+ void testInitializationOptions() {
LsStreamConnectionProvider provider = new LsStreamConnectionProvider();
InitializationOptions options = (InitializationOptions) provider.getInitializationOptions(null);
@@ -21,7 +21,7 @@ public void testInitializationOptions() {
}
@Test
- public void testStartLanguageServer() throws IOException {
+ void testStartLanguageServer() throws IOException {
LsStreamConnectionProvider provider = new LsStreamConnectionProvider();
try {
provider.start();
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
index 0dd2cd8d..907771d2 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
@@ -42,7 +42,9 @@ public void start(BundleContext context) throws Exception {
@Override
public void stop(BundleContext context) throws Exception {
-
+ if (copilotLanguageServer != null) {
+ copilotLanguageServer.stop();
+ }
}
@SuppressWarnings("restriction")
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
index eab1a722..c257c36b 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
@@ -41,4 +41,11 @@ public CompletableFuture checkStatus(Boolean localCheckOnly) {
return this.languageServerWrapper.execute(fn);
}
+ /**
+ * Stop the language server.
+ */
+ public void stop() {
+ this.languageServerWrapper.stop();
+ }
+
}
diff --git a/target-platform.target b/target-platform.target
index 8fac5a31..220747c4 100644
--- a/target-platform.target
+++ b/target-platform.target
@@ -11,7 +11,7 @@
-
+
@@ -42,6 +42,12 @@
5.14.2
+ org.mockito
+ mockito-junit-jupiter
+ 5.14.2
+
+
+
org.objenesis
objenesis
3.4
From f52f4f0b357379fc888604359a985e87fd69b583 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Mon, 9 Dec 2024 13:27:15 +0800
Subject: [PATCH 012/690] feat - Early startup the Core plugin from UI plugin
(#14)
---
.../.classpath | 11 ++++++
.../.project | 34 +++++++++++++++++++
.../.settings/org.eclipse.jdt.core.prefs | 9 +++++
.../.settings/org.eclipse.m2e.core.prefs | 4 +++
.../META-INF/MANIFEST.MF | 17 ++++++++++
.../build.properties | 5 +++
com.microsoft.copilot.eclipse.ui.test/pom.xml | 17 ++++++++++
.../copilot/eclipse/ui/CopilotUiTests.java | 22 ++++++++++++
.../META-INF/MANIFEST.MF | 6 ++--
com.microsoft.copilot.eclipse.ui/plugin.xml | 6 ++++
.../{CopilotUiPlugin.java => CopilotUi.java} | 7 ++--
.../microsoft/copilot/eclipse/ui/StartUp.java | 15 ++++++++
pom.xml | 5 ++-
13 files changed, 153 insertions(+), 5 deletions(-)
create mode 100644 com.microsoft.copilot.eclipse.ui.test/.classpath
create mode 100644 com.microsoft.copilot.eclipse.ui.test/.project
create mode 100644 com.microsoft.copilot.eclipse.ui.test/.settings/org.eclipse.jdt.core.prefs
create mode 100644 com.microsoft.copilot.eclipse.ui.test/.settings/org.eclipse.m2e.core.prefs
create mode 100644 com.microsoft.copilot.eclipse.ui.test/META-INF/MANIFEST.MF
create mode 100644 com.microsoft.copilot.eclipse.ui.test/build.properties
create mode 100644 com.microsoft.copilot.eclipse.ui.test/pom.xml
create mode 100644 com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/CopilotUiTests.java
rename com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/{CopilotUiPlugin.java => CopilotUi.java} (63%)
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/StartUp.java
diff --git a/com.microsoft.copilot.eclipse.ui.test/.classpath b/com.microsoft.copilot.eclipse.ui.test/.classpath
new file mode 100644
index 00000000..38f401ad
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui.test/.classpath
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.ui.test/.project b/com.microsoft.copilot.eclipse.ui.test/.project
new file mode 100644
index 00000000..03fbd5a9
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui.test/.project
@@ -0,0 +1,34 @@
+
+
+ com.microsoft.copilot.eclipse.ui.test
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/com.microsoft.copilot.eclipse.ui.test/.settings/org.eclipse.jdt.core.prefs b/com.microsoft.copilot.eclipse.ui.test/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000..62ef3488
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui.test/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,9 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=17
diff --git a/com.microsoft.copilot.eclipse.ui.test/.settings/org.eclipse.m2e.core.prefs b/com.microsoft.copilot.eclipse.ui.test/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 00000000..f897a7f1
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui.test/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/com.microsoft.copilot.eclipse.ui.test/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.ui.test/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..7d1c3ea9
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui.test/META-INF/MANIFEST.MF
@@ -0,0 +1,17 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: com.microsoft.copilot.eclipse.ui.test
+Bundle-SymbolicName: com.microsoft.copilot.eclipse.ui.test;singleton:=true
+Bundle-Version: 0.1.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Automatic-Module-Name: com.microsoft.copilot.eclipse.ui.test
+Import-Package: org.objenesis;version="[3.4.0,4.0.0)",
+ org.osgi.framework;version="[1.10.0,2.0.0)"
+Require-Bundle: org.mockito.mockito-core;bundle-version="5.14.2",
+ org.eclipse.lsp4e;bundle-version="0.18.12",
+ org.eclipse.jdt.annotation;bundle-version="2.3.0",
+ junit-jupiter-api;bundle-version="5.11.0",
+ org.mockito.junit-jupiter;bundle-version="5.14.2",
+ com.microsoft.copilot.eclipse.core;bundle-version="0.1.0",
+ com.microsoft.copilot.eclipse.ui;bundle-version="0.1.0",
+ org.eclipse.core.runtime;bundle-version="3.31.100"
diff --git a/com.microsoft.copilot.eclipse.ui.test/build.properties b/com.microsoft.copilot.eclipse.ui.test/build.properties
new file mode 100644
index 00000000..100b378a
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui.test/build.properties
@@ -0,0 +1,5 @@
+source.. = src
+output.. = target/classes
+bin.includes = META-INF/,\
+ .
+
diff --git a/com.microsoft.copilot.eclipse.ui.test/pom.xml b/com.microsoft.copilot.eclipse.ui.test/pom.xml
new file mode 100644
index 00000000..6c98c002
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui.test/pom.xml
@@ -0,0 +1,17 @@
+
+ 4.0.0
+
+ com.microsoft.copilot.eclipse
+ github-copilot-for-eclipse
+ 0.1.0-SNAPSHOT
+
+ com.microsoft.copilot.eclipse.ui.test
+ eclipse-plugin
+ ${base.name} :: UI Tests
+
+
+ true
+
+
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/CopilotUiTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/CopilotUiTests.java
new file mode 100644
index 00000000..14409d29
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/CopilotUiTests.java
@@ -0,0 +1,22 @@
+package com.microsoft.copilot.eclipse.ui;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.eclipse.core.runtime.Platform;
+import org.junit.jupiter.api.Test;
+import org.osgi.framework.Bundle;
+
+class CopilotUiTests {
+
+ @Test
+ void testCopilotCoreWakeUp() throws Exception {
+ CopilotUi ui = new CopilotUi();
+ ui.start(null);
+
+ Bundle bundle = Platform.getBundle("com.microsoft.copilot.eclipse.core");
+
+ assertNotNull(bundle);
+ assertEquals(Bundle.ACTIVE, bundle.getState());
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
index b46c543d..47b63666 100644
--- a/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
@@ -3,9 +3,11 @@ Bundle-ManifestVersion: 2
Bundle-Name: com.microsoft.copilot.eclipse.ui
Bundle-SymbolicName: com.microsoft.copilot.eclipse.ui;singleton:=true
Bundle-Version: 0.1.0.qualifier
-Bundle-Activator: com.microsoft.copilot.eclipse.ui.CopilotUiPlugin
+Export-Package: com.microsoft.copilot.eclipse.ui
+Bundle-Activator: com.microsoft.copilot.eclipse.ui.CopilotUi
Bundle-RequiredExecutionEnvironment: JavaSE-17
Automatic-Module-Name: com.microsoft.copilot.eclipse.ui
Bundle-ActivationPolicy: lazy
Import-Package: org.osgi.framework;version="[1.10.0,2.0.0)"
-Require-Bundle: com.microsoft.copilot.eclipse.core;bundle-version="0.1.0"
+Require-Bundle: com.microsoft.copilot.eclipse.core;bundle-version="0.1.0",
+ org.eclipse.ui.ide
diff --git a/com.microsoft.copilot.eclipse.ui/plugin.xml b/com.microsoft.copilot.eclipse.ui/plugin.xml
index f422d55d..44ad4023 100644
--- a/com.microsoft.copilot.eclipse.ui/plugin.xml
+++ b/com.microsoft.copilot.eclipse.ui/plugin.xml
@@ -1,4 +1,10 @@
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUiPlugin.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
similarity index 63%
rename from com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUiPlugin.java
rename to com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
index 9a0a59cd..b13f40f9 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUiPlugin.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
@@ -3,14 +3,17 @@
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+
/**
* Activator class for the Copilot UI plugin.
*/
-public class CopilotUiPlugin implements BundleActivator {
+public class CopilotUi implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
-
+ // wake up the Core plugin by calling a method from it.
+ CopilotCore.getPlugin();
}
@Override
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/StartUp.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/StartUp.java
new file mode 100644
index 00000000..e53d37ee
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/StartUp.java
@@ -0,0 +1,15 @@
+package com.microsoft.copilot.eclipse.ui;
+
+import org.eclipse.ui.IStartup;
+
+/**
+ * Early startup the Copilot for Eclipse plugin.
+ */
+public class StartUp implements IStartup {
+
+ @Override
+ public void earlyStartup() {
+ // do nothing.
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 90293ef3..1ff5f28c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,8 +18,11 @@
com.microsoft.copilot.eclipse.core
- com.microsoft.copilot.eclipse.core.test
com.microsoft.copilot.eclipse.ui
+
+
+ com.microsoft.copilot.eclipse.core.test
+ com.microsoft.copilot.eclipse.ui.test
From 06d77761b84fe4d3c5896d92e7e08e4d23dd1bd7 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Mon, 9 Dec 2024 16:31:38 +0800
Subject: [PATCH 013/690] build - Add Mac and Windows for CI (#15)
---
.github/workflows/ci.yml | 5 ++++-
.../copilot-agent/package.json | 2 +-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 125b4a99..c3274a63 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,7 +9,10 @@ on:
jobs:
build:
- runs-on: ubuntu-latest
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
diff --git a/com.microsoft.copilot.eclipse.core/copilot-agent/package.json b/com.microsoft.copilot.eclipse.core/copilot-agent/package.json
index 27e794ea..0c16fe6a 100644
--- a/com.microsoft.copilot.eclipse.core/copilot-agent/package.json
+++ b/com.microsoft.copilot.eclipse.core/copilot-agent/package.json
@@ -9,7 +9,7 @@
"url": "https://github.com/microsoft/copilot-eclipse.git"
},
"scripts": {
- "postinstall": "npx --yes copyfiles -u 3 'node_modules/@github/copilot-language-server/native/**/*' '.'"
+ "postinstall": "npx --yes copyfiles -u 3 \"node_modules/@github/copilot-language-server/native/**/*\" \".\""
},
"dependencies": {
"@github/copilot-language-server": "^1.244.0"
From 5df8208425d6cac114be914dd7c0762762799375 Mon Sep 17 00:00:00 2001
From: ethanyhou <149548697+ethanyhou@users.noreply.github.com>
Date: Tue, 10 Dec 2024 10:06:09 +0800
Subject: [PATCH 014/690] fix - Updated the parsing method to get the agent
across platforms. (#17)
---
.../eclipse/core/lsp/LsStreamConnectionProvider.java | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
index 4c3daf39..bdb88ec2 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
@@ -3,6 +3,7 @@
import java.io.File;
import java.io.IOException;
import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -10,6 +11,7 @@
import java.util.List;
import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.URIUtil;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider;
import org.osgi.framework.Bundle;
@@ -93,7 +95,12 @@ public void start() throws IOException {
return null;
}
- return Path.of(FileLocator.toFileURL(url).getPath());
+ try {
+ return URIUtil.toFile(URIUtil.toURI(FileLocator.toFileURL(url))).toPath();
+ } catch (URISyntaxException | IOException e) {
+ // TODO: Log exception via telemetry.
+ return null;
+ }
}
}
From ac55bd5f06f2ba810abc9651de196e6f8465446c Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Tue, 10 Dec 2024 15:47:22 +0800
Subject: [PATCH 015/690] feat - Initialize the editor lifecycle listeners for
completion (#19)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
.settings/dict | 1 +
.../META-INF/MANIFEST.MF | 3 +-
.../copilot/eclipse/core/CopilotCore.java | 2 +-
.../lsp/CopilotLanguageServerConnection.java | 28 ++++-
.../core/lsp/LsStreamConnectionProvider.java | 1 -
.../META-INF/MANIFEST.MF | 6 +-
.../ui/completion/EditorManagerTests.java | 35 ++++++
.../META-INF/MANIFEST.MF | 12 ++-
.../copilot/eclipse/ui/CopilotUi.java | 64 ++++++++++-
.../ui/completion/CompletionHandler.java | 102 ++++++++++++++++++
.../completion/EditorLifecycleListener.java | 63 +++++++++++
.../eclipse/ui/completion/EditorsManager.java | 60 +++++++++++
.../copilot/eclipse/ui/utils/SwtUtils.java | 71 ++++++++++++
.../copilot/eclipse/ui/utils/UiUtils.java | 34 ++++++
14 files changed, 472 insertions(+), 10 deletions(-)
create mode 100644 com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/EditorManagerTests.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorLifecycleListener.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorsManager.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
diff --git a/.settings/dict b/.settings/dict
index dd8d6872..a003f6fd 100644
--- a/.settings/dict
+++ b/.settings/dict
@@ -1,3 +1,4 @@
copilot
plugin
telemetry
+lifecycle
diff --git a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
index ea216d43..ab0e2d6d 100644
--- a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
@@ -16,4 +16,5 @@ Require-Bundle: org.eclipse.lsp4e;bundle-version="0.18.12",
org.eclipse.lsp4j;bundle-version="0.23.1",
org.eclipse.lsp4j.jsonrpc;bundle-version="0.23.1",
org.apache.commons.lang3;bundle-version="3.17.0",
- org.eclipse.jdt.annotation;bundle-version="2.3.0"
+ org.eclipse.jdt.annotation;bundle-version="2.3.0",
+ org.eclipse.jface.text
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
index 907771d2..dbdf1297 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
@@ -31,7 +31,7 @@ public CopilotCore() {
COPILOT_CORE_PLUGIN = this;
}
- public static Plugin getPlugin() {
+ public static CopilotCore getPlugin() {
return COPILOT_CORE_PLUGIN;
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
index c257c36b..0268b230 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
@@ -1,8 +1,11 @@
package com.microsoft.copilot.eclipse.core.lsp;
+import java.io.IOException;
+import java.net.URI;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
+import org.eclipse.jface.text.IDocument;
import org.eclipse.lsp4e.LanguageServerWrapper;
import org.eclipse.lsp4j.services.LanguageServer;
@@ -12,7 +15,7 @@
/**
* Language Server for Copilot agent.
*/
-@SuppressWarnings("restriction")
+@SuppressWarnings({ "restriction", "null" })
public class CopilotLanguageServerConnection {
public static final String SERVER_ID = "com.microsoft.copilot.eclipse.ls";
@@ -28,10 +31,31 @@ public CopilotLanguageServerConnection(LanguageServerWrapper languageServerWrapp
this.languageServerWrapper = languageServerWrapper;
}
+ /**
+ * Connect the document to the language server. The LSP4E will take care of all the document lifecycle events after
+ * that.
+ */
+ public void connectDocument(IDocument document) throws IOException {
+ this.languageServerWrapper.connectDocument(document);
+ }
+
+ /**
+ * Disconnect the document from the language server.
+ */
+ public void disconnectDocument(URI uri) {
+ this.languageServerWrapper.disconnect(uri);
+ }
+
+ /**
+ * Get the document version for the given URI.
+ */
+ public int getDocumentVersion(URI uri) {
+ return this.languageServerWrapper.getTextDocumentVersion(uri);
+ }
+
/**
* Check the login status for current machine.
*/
- @SuppressWarnings("null")
public CompletableFuture checkStatus(Boolean localCheckOnly) {
Function> fn = server -> {
CheckStatusParams param = new CheckStatusParams();
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
index bdb88ec2..48ba83c5 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
@@ -59,7 +59,6 @@ public void start() throws IOException {
commands.add(binary.toString());
commands.add("--stdio");
this.setCommands(commands);
- // TODO: will it have permission issue on Unix based OS?
super.start();
}
diff --git a/com.microsoft.copilot.eclipse.ui.test/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.ui.test/META-INF/MANIFEST.MF
index 7d1c3ea9..15589da4 100644
--- a/com.microsoft.copilot.eclipse.ui.test/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.ui.test/META-INF/MANIFEST.MF
@@ -14,4 +14,8 @@ Require-Bundle: org.mockito.mockito-core;bundle-version="5.14.2",
org.mockito.junit-jupiter;bundle-version="5.14.2",
com.microsoft.copilot.eclipse.core;bundle-version="0.1.0",
com.microsoft.copilot.eclipse.ui;bundle-version="0.1.0",
- org.eclipse.core.runtime;bundle-version="3.31.100"
+ org.eclipse.core.runtime;bundle-version="3.31.100",
+ org.eclipse.ui;bundle-version="3.206.100",
+ org.eclipse.ui.ide,
+ org.eclipse.ui.workbench.texteditor,
+ org.eclipse.jface.text;bundle-version="3.25.200"
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/EditorManagerTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/EditorManagerTests.java
new file mode 100644
index 00000000..de006db2
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/EditorManagerTests.java
@@ -0,0 +1,35 @@
+package com.microsoft.copilot.eclipse.ui.completion;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.mock;
+
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+
+@ExtendWith(MockitoExtension.class)
+class EditorManagerTests {
+
+ @Test
+ void testCreateHandlerForNull() {
+ CopilotLanguageServerConnection mockServer = mock(CopilotLanguageServerConnection.class);
+ EditorsManager manager = new EditorsManager(mockServer);
+
+ assertNull(manager.getOrCreateCompletionHandlerFor(null));
+ }
+
+ @Test
+ void getOrCreateCompletionHandlerForReturnsNewHandlerWhenNotPresent() {
+ ITextEditor mockEditor = mock(ITextEditor.class);
+ CopilotLanguageServerConnection mockServer = mock(CopilotLanguageServerConnection.class);
+
+ EditorsManager manager = new EditorsManager(mockServer);
+ CompletionHandler handler = manager.getOrCreateCompletionHandlerFor(mockEditor);
+
+ assertNotNull(handler);
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
index 47b63666..b1a4e699 100644
--- a/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
@@ -3,11 +3,19 @@ Bundle-ManifestVersion: 2
Bundle-Name: com.microsoft.copilot.eclipse.ui
Bundle-SymbolicName: com.microsoft.copilot.eclipse.ui;singleton:=true
Bundle-Version: 0.1.0.qualifier
-Export-Package: com.microsoft.copilot.eclipse.ui
+Export-Package: com.microsoft.copilot.eclipse.ui,
+ com.microsoft.copilot.eclipse.ui.completion
Bundle-Activator: com.microsoft.copilot.eclipse.ui.CopilotUi
Bundle-RequiredExecutionEnvironment: JavaSE-17
Automatic-Module-Name: com.microsoft.copilot.eclipse.ui
Bundle-ActivationPolicy: lazy
Import-Package: org.osgi.framework;version="[1.10.0,2.0.0)"
Require-Bundle: com.microsoft.copilot.eclipse.core;bundle-version="0.1.0",
- org.eclipse.ui.ide
+ org.eclipse.ui.ide,
+ org.eclipse.ui.workbench.texteditor,
+ org.eclipse.ui;bundle-version="3.206.100",
+ org.eclipse.jface.text;bundle-version="3.25.200",
+ org.eclipse.core.runtime;bundle-version="3.31.100",
+ org.eclipse.jdt.annotation,
+ org.eclipse.core.resources,
+ org.eclipse.lsp4e
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
index b13f40f9..6c0a0d1d 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
@@ -1,24 +1,84 @@
package com.microsoft.copilot.eclipse.ui;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.texteditor.ITextEditor;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.ui.completion.EditorLifecycleListener;
+import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
+import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
/**
* Activator class for the Copilot UI plugin.
*/
public class CopilotUi implements BundleActivator {
+ private static final int RETRY_COUNT = 30;
+ private EditorLifecycleListener editorLifecycleListener;
+ private EditorsManager editorsManager;
+
@Override
public void start(BundleContext context) throws Exception {
- // wake up the Core plugin by calling a method from it.
- CopilotCore.getPlugin();
+ // wake up Core plugin and wait until copilot LS is ready
+ // TODO: check if we can improve logic here, for example, use a listener to wait for LS ready.
+ CopilotLanguageServerConnection connection = null;
+ for (int i = 0; i < RETRY_COUNT; i++) {
+ connection = CopilotCore.getPlugin().getCopilotLanguageServer();
+ if (connection != null) {
+ break;
+ }
+ Thread.sleep(1000);
+ }
+ if (connection == null) {
+ // TODO: log & send telemetry
+ throw new IllegalStateException("Copilot language server is not ready.");
+ }
+
+ this.editorsManager = new EditorsManager(connection);
+ this.editorLifecycleListener = new EditorLifecycleListener(editorsManager);
+
+ registerPartListener();
+
+ // Initialize the completion handler for the active editor in case we miss the event
+ // to initialize it.
+ initComletionHandlerForActiveEditor();
}
@Override
public void stop(BundleContext context) throws Exception {
+ unregisterPartListener();
+ if (this.editorsManager != null) {
+ this.editorsManager.dispose();
+ }
+ }
+
+ private void registerPartListener() {
+ IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
+ for (IWorkbenchWindow window : windows) {
+ window.getPartService().addPartListener(this.editorLifecycleListener);
+ }
+ }
+
+ private void initComletionHandlerForActiveEditor() {
+ IEditorPart editorPart = SwtUtils.getActiveEditorPart();
+ if (editorPart != null) {
+ ITextEditor textEditor = editorPart.getAdapter(ITextEditor.class);
+ if (textEditor != null) {
+ this.editorsManager.getOrCreateCompletionHandlerFor(textEditor);
+ }
+ }
+ }
+ private void unregisterPartListener() {
+ IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
+ for (IWorkbenchWindow window : windows) {
+ window.getPartService().removePartListener(this.editorLifecycleListener);
+ }
}
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
new file mode 100644
index 00000000..7ef0ace3
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
@@ -0,0 +1,102 @@
+package com.microsoft.copilot.eclipse.ui.completion;
+
+import java.io.IOException;
+import java.net.URI;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.ITextOperationTarget;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.TextEvent;
+import org.eclipse.lsp4e.LSPEclipseUtils;
+import org.eclipse.swt.custom.CaretEvent;
+import org.eclipse.swt.custom.CaretListener;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
+import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
+
+/**
+ * Handle completion for an ITextEditor.
+ */
+public class CompletionHandler implements ITextListener, CaretListener {
+
+ private CopilotLanguageServerConnection lsConnection;
+ private ITextEditor editor;
+ private ITextViewer textViewer;
+ private IDocument document;
+ private URI documentUri;
+ private int documentVersion;
+
+ /**
+ * Creates a new completion handler.
+ */
+ public CompletionHandler(CopilotLanguageServerConnection lsConnection, ITextEditor editor) {
+ this.lsConnection = lsConnection;
+ this.editor = editor;
+ this.textViewer = (ITextViewer) this.editor.getAdapter(ITextOperationTarget.class);
+ this.document = LSPEclipseUtils.getDocument(editor);
+ this.documentUri = UiUtils.getUriFromTextEditor(editor);
+ this.documentVersion = 0;
+ try {
+ lsConnection.connectDocument(this.document);
+ } catch (IOException e) {
+ // TODO: log & send telemetry
+ return;
+ }
+ registerListeners();
+ }
+
+ @Override
+ public void caretMoved(CaretEvent event) {
+ // it's guaranteed that the document change event comes earlier than caret
+ // change event. See org.eclipse.swt.custom.StyledText#modifyContent()
+ int currentVersion = this.lsConnection.getDocumentVersion(this.documentUri);
+ if (currentVersion == this.documentVersion) {
+ // if the caret position is changed without document version change, we should remove the ghost text.
+ // TODO: remove ghost text
+ } else {
+ this.documentVersion = currentVersion;
+ // TODO: trigger completion
+ }
+
+ }
+
+ @Override
+ public void textChanged(TextEvent event) {
+ // this event comes earlier than caret change event. So we should check if the typed characters
+ // are the same as the ghost. Then determine whether a re-redering or a new completion
+ // request is needed.
+ // TODO: check changed text
+ }
+
+ /**
+ * Disposes the resources of this completion handler.
+ */
+ public void dispose() {
+ lsConnection.disconnectDocument(this.documentUri);
+ if (this.textViewer != null) {
+ SwtUtils.invokeOnDisplayThread(() -> {
+ this.textViewer.getTextWidget().removeCaretListener(this);
+ this.textViewer.removeTextListener(this);
+ });
+ }
+
+ }
+
+ void registerListeners() {
+ // if the text viewer is null, we will not register listeners.
+ // the side effect is that the completion will not be triggered for this editor.
+ if (this.textViewer == null) {
+ // TODO: log & send telemetry
+ return;
+ }
+
+ SwtUtils.invokeOnDisplayThread(() -> {
+ this.textViewer.getTextWidget().addCaretListener(this);
+ this.textViewer.addTextListener(this);
+ });
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorLifecycleListener.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorLifecycleListener.java
new file mode 100644
index 00000000..4aba250d
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorLifecycleListener.java
@@ -0,0 +1,63 @@
+package com.microsoft.copilot.eclipse.ui.completion;
+
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Listen to the lifecycle event of an editor parts.
+ */
+public class EditorLifecycleListener implements IPartListener {
+
+ private EditorsManager manager;
+
+ /**
+ * Creates a new EditorLifecycleListener.
+ */
+ public EditorLifecycleListener(EditorsManager manager) {
+ this.manager = manager;
+ }
+
+ @Override
+ public void partActivated(IWorkbenchPart part) {
+ createCompletionHandlerFor(part);
+
+ }
+
+ @Override
+ public void partBroughtToTop(IWorkbenchPart part) {
+ // do nothing.
+ }
+
+ @Override
+ public void partClosed(IWorkbenchPart part) {
+ disposeCompletionHandlerFor(part);
+ }
+
+ @Override
+ public void partDeactivated(IWorkbenchPart part) {
+ // do nothing.
+ }
+
+ @Override
+ public void partOpened(IWorkbenchPart part) {
+ // do nothing.
+ }
+
+ void createCompletionHandlerFor(IWorkbenchPart part) {
+ ITextEditor editor = part.getAdapter(ITextEditor.class);
+ if (editor == null) {
+ return;
+ }
+ manager.getOrCreateCompletionHandlerFor(editor);
+ }
+
+ void disposeCompletionHandlerFor(IWorkbenchPart part) {
+ ITextEditor editor = part.getAdapter(ITextEditor.class);
+ if (editor == null) {
+ return;
+ }
+ manager.disposeCompletionHandlerFor(editor);
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorsManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorsManager.java
new file mode 100644
index 00000000..f38e408c
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorsManager.java
@@ -0,0 +1,60 @@
+package com.microsoft.copilot.eclipse.ui.completion;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+
+/**
+ * Manages the completion handlers for all available ITextEditors.
+ */
+public class EditorsManager {
+
+ private CopilotLanguageServerConnection languageServer;
+ private Map editorMap;
+
+ /**
+ * Creates a new EditorManager.
+ */
+ public EditorsManager(CopilotLanguageServerConnection languageServer) {
+ this.languageServer = languageServer;
+ this.editorMap = new ConcurrentHashMap<>();
+ }
+
+ /**
+ * Gets the {@link com.microsoft.copilot.eclipse.ui.completion.CompletionHandler CompletionHandler} for the given
+ * ITextEditor. If it does not exist, a new one will be created. Returns null if the editor is
+ * null.
+ */
+ public CompletionHandler getOrCreateCompletionHandlerFor(ITextEditor editor) {
+ if (editor == null) {
+ return null;
+ }
+
+ return editorMap.computeIfAbsent(editor, edt -> new CompletionHandler(this.languageServer, edt));
+ }
+
+ /**
+ * Disposes the {@link com.microsoft.copilot.eclipse.ui.completion.CompletionHandler CompletionHandler} for the given
+ * ITextEditor.
+ */
+ public void disposeCompletionHandlerFor(ITextEditor editor) {
+ CompletionHandler handler = editorMap.remove(editor);
+ if (handler != null) {
+ handler.dispose();
+ }
+ }
+
+ /**
+ * Dispose all the handlers.
+ */
+ public void dispose() {
+ for (CompletionHandler handler : this.editorMap.values()) {
+ handler.dispose();
+ }
+ this.editorMap.clear();
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java
new file mode 100644
index 00000000..b745f8ed
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java
@@ -0,0 +1,71 @@
+package com.microsoft.copilot.eclipse.ui.utils;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Utilities for SWT. *
+ */
+public class SwtUtils {
+
+ private SwtUtils() {
+ // prevent instantiation
+ }
+
+ /**
+ * Invokes the given runnable on the display thread.
+ */
+ public static void invokeOnDisplayThread(Runnable runnable) {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow[] windows = workbench.getWorkbenchWindows();
+ if (windows != null && windows.length > 0) {
+ Display display = windows[0].getShell().getDisplay();
+ display.syncExec(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+
+ /**
+ * Invokes the given runnable on the display thread.
+ *
+ * @param runnable the runnable to invoke
+ * @param control the control used for the display
+ */
+ public static void invokeOnDisplayThread(Runnable runnable, Control control) {
+ if (Objects.isNull(control)) {
+ invokeOnDisplayThread(runnable);
+ } else {
+ Display display = control.getDisplay();
+ if (display.getThread() == Thread.currentThread()) {
+ runnable.run();
+ } else {
+ display.syncExec(runnable);
+ }
+ }
+ }
+
+ /**
+ * Get the active editor part from workbench.
+ */
+ @Nullable
+ public static IEditorPart getActiveEditorPart() {
+ AtomicReference ref = new AtomicReference<>();
+ invokeOnDisplayThread(() -> {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ if (window != null) {
+ ref.set(window.getActivePage().getActiveEditor());
+ }
+ });
+ return ref.get();
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
new file mode 100644
index 00000000..2f609300
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
@@ -0,0 +1,34 @@
+package com.microsoft.copilot.eclipse.ui.utils;
+
+import java.net.URI;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Utilities for Eclipse UI.
+ */
+public class UiUtils {
+
+ private UiUtils() {
+ // prevent instantiation
+ }
+
+ /**
+ * Gets the URI of the file opened in the given text editor.
+ */
+ @Nullable
+ public static URI getUriFromTextEditor(ITextEditor editor) {
+ IEditorInput input = editor.getEditorInput();
+ if (input instanceof IFileEditorInput fileInput) {
+ IFile file = fileInput.getFile();
+ return file.getLocationURI();
+ }
+
+ return null;
+ }
+
+}
From 6f2f67e925cc6aede193f21f530eea9425a21c38 Mon Sep 17 00:00:00 2001
From: ethanyhou <149548697+ethanyhou@users.noreply.github.com>
Date: Wed, 11 Dec 2024 13:09:21 +0800
Subject: [PATCH 016/690] feat - Added draft scaffold for the GitHub copilot
menu. (#21)
---
.../icons/copilot.png | Bin 0 -> 707 bytes
com.microsoft.copilot.eclipse.ui/plugin.xml | 51 ++++++++++++++++
.../copilot/eclipse/ui/Constants.java | 9 +++
.../ui/handlers/ShowStatusBarMenuHandler.java | 55 ++++++++++++++++++
.../copilot/eclipse/ui/utils/UiUtils.java | 21 +++++++
5 files changed, 136 insertions(+)
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/copilot.png
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/Constants.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
diff --git a/com.microsoft.copilot.eclipse.ui/icons/copilot.png b/com.microsoft.copilot.eclipse.ui/icons/copilot.png
new file mode 100644
index 0000000000000000000000000000000000000000..31996ba7e667e0ea2d414b353a40a12ce7c41e53
GIT binary patch
literal 707
zcmV;!0zCbRP)tAoU2Wys7|J>9NOJ9dQM-OW-MPk6goMey~7J%cNcYt8AM|)c$G1S
zoftr0Ba4bGb$~IBZ6C?e6S2KSl%pBY))c3;Kc1=;$VV
zPN#hn%Fa)wPc78eHiD5aXGT%FG)9?_KWBm~W-CRaB{^SqP>W??hM9$?vnx_wE?8V|
zp^%KSuUmVitjpK9)!Q*~h(w8&6yDux#p#UH>RynYY_r-|z%y5Sm0NwPSi32St-5rw
zZEKU!VRo2%oVmWy*|F5hY>TC&!@qf%t<~k~(dO&x_xeedq-CqfQK!5>n4v0Mc4Wua
zW~0)I&&zJp*cjsAd)M$~m&8oe-b9n)R(9M&sN_q~*l5hvdcDInfO|Zo!cTzMO`efE
zqQGjTq)xW!Zk5VEgw{Zp;Zc3rslv;FuZOV9)0w@~im{q_t#;z@@_wej;_mU+=j+bi
zy>KxHFs?scc
z5WbjVRB{prM_hP_5I=w&`5Q0^Y-?s
z^7kpSu(z@jWKhy{DYdlp^RM)?D0T`H7GPlH*5mTE3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/Constants.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/Constants.java
new file mode 100644
index 00000000..4a265f43
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/Constants.java
@@ -0,0 +1,9 @@
+package com.microsoft.copilot.eclipse.ui;
+
+/**
+ * A class to hold all the public constants used in the GitHub Copilot UI.
+ */
+public class Constants {
+ public static final int TOOLBAR_ICON_WIDTH_IN_PIEXL = 16;
+ public static final int TOOLBAR_ICON_HEIGHT_IN_PIEXL = 16;
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
new file mode 100644
index 00000000..1af47c32
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
@@ -0,0 +1,55 @@
+package com.microsoft.copilot.eclipse.ui.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+import com.microsoft.copilot.eclipse.ui.Constants;
+import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
+
+/**
+ * Handler for showing GitHub Copilot status bar menu.
+ */
+public class ShowStatusBarMenuHandler extends AbstractHandler {
+
+ /**
+ * Render the status bar menu based on the logged-in state.
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+
+ Shell shell = PlatformUI.getWorkbench().getDisplay().getActiveShell();
+ MenuManager menuManager = new MenuManager();
+ ImageDescriptor icon = UiUtils.resizeIcon("/icons/copilot.png", Constants.TOOLBAR_ICON_WIDTH_IN_PIEXL,
+ Constants.TOOLBAR_ICON_HEIGHT_IN_PIEXL);
+
+ // TODO: Add GitHub sign-in states to the menu
+ Action signInAction = new Action("Sign In to GitHub", icon) {
+ @Override
+ public void run() {
+ // Handle sign-in action
+ }
+ };
+
+ // TODO: Add GitHub sign-out states to the menu
+ Action signOutAction = new Action("Sign Out from GitHub", icon) {
+ @Override
+ public void run() {
+ // Handle sign-out action
+ }
+ };
+
+ menuManager.add(signInAction);
+ menuManager.add(signOutAction);
+
+ Menu menu = menuManager.createContextMenu(shell);
+ menu.setVisible(true);
+ return null;
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
index 2f609300..992eefe8 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
@@ -4,6 +4,11 @@
import org.eclipse.core.resources.IFile;
import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;
@@ -31,4 +36,20 @@ public static URI getUriFromTextEditor(ITextEditor editor) {
return null;
}
+ /**
+ * Resizes the icon at the given path to the given width and height.
+ * Icon size is 16x16 by default, which is the recommended size for toolbar icons.
+ * For more details: https://eclipse-platform.github.io/ui-best-practices/#toolbar
+ */
+ public static ImageDescriptor resizeIcon(String path, int width, int height) {
+ ImageLoader loader = new ImageLoader();
+ ImageData[] imageDataArray = loader.load(UiUtils.class.getResourceAsStream(path));
+ if (imageDataArray.length > 0) {
+ ImageData imageData = imageDataArray[0].scaledTo(width, height);
+ Image image = new Image(Display.getDefault(), imageData);
+ return ImageDescriptor.createFromImage(image);
+ }
+ return null;
+ }
+
}
From 7333c43437e9f1341405d87cbe45ef829b642e9f Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Wed, 11 Dec 2024 15:07:27 +0800
Subject: [PATCH 017/690] feat - Trigger inline completion on document change
(#23)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
.../copilot/eclipse/core/Constants.java | 13 ++
.../core/lsp/CopilotLanguageServer.java | 8 ++
.../lsp/CopilotLanguageServerConnection.java | 13 +-
.../core/lsp/protocol/CompletionDocument.java | 106 +++++++++++++++
.../core/lsp/protocol/CompletionItem.java | 127 ++++++++++++++++++
.../core/lsp/protocol/CompletionParams.java | 70 ++++++++++
.../core/lsp/protocol/CompletionResult.java | 58 ++++++++
.../ui/completion/CompletionJobTests.java | 72 ++++++++++
.../META-INF/MANIFEST.MF | 3 +-
.../ui/{Constants.java => UiConstants.java} | 7 +-
.../ui/completion/CompletionHandler.java | 88 +++++++++++-
.../eclipse/ui/completion/CompletionJob.java | 66 +++++++++
.../ui/handlers/ShowStatusBarMenuHandler.java | 6 +-
.../copilot/eclipse/ui/utils/UiUtils.java | 21 ++-
14 files changed, 646 insertions(+), 12 deletions(-)
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionDocument.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionItem.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionParams.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionResult.java
create mode 100644 com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/CompletionJobTests.java
rename com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/{Constants.java => UiConstants.java} (73%)
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionJob.java
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java
new file mode 100644
index 00000000..9894b8e0
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java
@@ -0,0 +1,13 @@
+package com.microsoft.copilot.eclipse.core;
+
+/**
+ * A class to hold all the public constants used in the GitHub Copilot core.
+ */
+public class Constants {
+
+ private Constants() {
+ // prevent instantiation
+ }
+
+ public static final String PLUGIN_ID = "com.microsoft.copilot.eclipse";
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java
index 1945d79f..8b6e8e45 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java
@@ -7,6 +7,8 @@
import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CheckStatusParams;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
/**
* Interface for Copilot Language Server.
@@ -19,4 +21,10 @@ public interface CopilotLanguageServer extends LanguageServer {
@JsonRequest
CompletableFuture checkStatus(CheckStatusParams param);
+ /**
+ * Get single completion for the given parameters.
+ */
+ @JsonRequest
+ CompletableFuture getCompletions(CompletionParams params);
+
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
index 0268b230..ac1d23b1 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
@@ -11,11 +11,13 @@
import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CheckStatusParams;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
/**
* Language Server for Copilot agent.
*/
-@SuppressWarnings({ "restriction", "null" })
+@SuppressWarnings({ "restriction" })
public class CopilotLanguageServerConnection {
public static final String SERVER_ID = "com.microsoft.copilot.eclipse.ls";
@@ -65,6 +67,15 @@ public CompletableFuture checkStatus(Boolean localCheckOnly) {
return this.languageServerWrapper.execute(fn);
}
+ /**
+ * Get single completion for the given parameters.
+ */
+ public CompletableFuture getCompletions(CompletionParams params) {
+ Function> fn = server -> ((CopilotLanguageServer) server)
+ .getCompletions(params);
+ return this.languageServerWrapper.execute(fn);
+ }
+
/**
* Stop the language server.
*/
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionDocument.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionDocument.java
new file mode 100644
index 00000000..2e718667
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionDocument.java
@@ -0,0 +1,106 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Objects;
+
+import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * Document information for completion.
+ */
+public class CompletionDocument {
+
+ @NonNull
+ private String uri;
+
+ @NonNull
+ private Position position;
+
+ private boolean insertSpaces;
+
+ private int tabSize;
+
+ private int version;
+
+ /**
+ * Create a new CompletionDocument.
+ */
+ public CompletionDocument(@NonNull String uri, @NonNull Position position) {
+ this.uri = uri;
+ this.position = position;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+
+ public Position getPosition() {
+ return position;
+ }
+
+ public void setPosition(Position position) {
+ this.position = position;
+ }
+
+ public boolean isInsertSpaces() {
+ return insertSpaces;
+ }
+
+ public void setInsertSpaces(boolean insertSpaces) {
+ this.insertSpaces = insertSpaces;
+ }
+
+ public int getTabSize() {
+ return tabSize;
+ }
+
+ public void setTabSize(int tabSize) {
+ this.tabSize = tabSize;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(insertSpaces, position, tabSize, uri, version);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ CompletionDocument other = (CompletionDocument) obj;
+ return insertSpaces == other.insertSpaces && Objects.equals(position, other.position) && tabSize == other.tabSize
+ && Objects.equals(uri, other.uri) && version == other.version;
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("uri", uri);
+ builder.add("position", position);
+ builder.add("insertSpaces", insertSpaces);
+ builder.add("tabSize", tabSize);
+ builder.add("version", version);
+ return builder.toString();
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionItem.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionItem.java
new file mode 100644
index 00000000..5d71cd2e
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionItem.java
@@ -0,0 +1,127 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Objects;
+
+import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.Range;
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * An item of a completion result list.
+ */
+public class CompletionItem {
+
+ @NonNull
+ private String uuid;
+
+ @NonNull
+ private String text;
+
+ @NonNull
+ private Range range;
+
+ @NonNull
+ private String displayText;
+
+ @NonNull
+ Position position;
+
+ @NonNull
+ private int docVersion;
+
+ /**
+ * Creates a new CompletionItem.
+ */
+ public CompletionItem(@NonNull String uuid, @NonNull String text, @NonNull Range range, @NonNull String displayText,
+ @NonNull Position position, @NonNull int docVersion) {
+ this.uuid = uuid;
+ this.text = text;
+ this.range = range;
+ this.displayText = displayText;
+ this.position = position;
+ this.docVersion = docVersion;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public Range getRange() {
+ return range;
+ }
+
+ public void setRange(Range range) {
+ this.range = range;
+ }
+
+ public String getDisplayText() {
+ return displayText;
+ }
+
+ public void setDisplayText(String displayText) {
+ this.displayText = displayText;
+ }
+
+ public Position getPosition() {
+ return position;
+ }
+
+ public void setPosition(Position position) {
+ this.position = position;
+ }
+
+ public int getDocVersion() {
+ return docVersion;
+ }
+
+ public void setDocVersion(int docVersion) {
+ this.docVersion = docVersion;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(displayText, docVersion, position, range, text, uuid);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ CompletionItem other = (CompletionItem) obj;
+ return Objects.equals(displayText, other.displayText) && docVersion == other.docVersion
+ && Objects.equals(position, other.position) && Objects.equals(range, other.range)
+ && Objects.equals(text, other.text) && Objects.equals(uuid, other.uuid);
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("uuid", uuid);
+ builder.add("text", text);
+ builder.add("range", range);
+ builder.add("displayText", displayText);
+ builder.add("position", position);
+ builder.add("docVersion", docVersion);
+ return builder.toString();
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionParams.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionParams.java
new file mode 100644
index 00000000..1884b4d1
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CompletionParams.java
@@ -0,0 +1,70 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Map;
+import java.util.Objects;
+
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * Parameter for getCompletion request.
+ */
+public class CompletionParams {
+
+ @NonNull
+ private CompletionDocument doc;
+
+ private Map
+ * Initiate the sign in process.
+ */
+ public CompletableFuture signInInitiate() {
+ Function> fn = (server) -> ((CopilotLanguageServer) server)
+ .signInInitiate(new NullParams());
+ return this.languageServerWrapper.execute(fn);
+ }
+
+ /**
+ * Please use the {@link AuthStatusManager#signInConfirm()} method instead.
+ * Confirm the sign in process.
+ */
+ public CompletableFuture signInConfirm(String userCode) {
+ Function> fn = (server) -> {
+ SignInConfirmParams param = new SignInConfirmParams(userCode);
+ return ((CopilotLanguageServer) server).signInConfirm(param);
+ };
+ return this.languageServerWrapper.execute(fn);
+ }
+
+ /**
+ * Please use the {@link AuthStatusManager#signOut()} method instead.
+ * Sign out from the GitHub Copilot.
+ */
+ public CompletableFuture signOut() {
+ Function> fn = (server) -> ((CopilotLanguageServer) server)
+ .signOut(new NullParams());
+ return this.languageServerWrapper.execute(fn);
+ }
+
/**
* Stop the language server.
*/
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/AuthStatusResult.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/AuthStatusResult.java
index 37c95e28..83a637e2 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/AuthStatusResult.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/AuthStatusResult.java
@@ -13,6 +13,7 @@ public class AuthStatusResult {
public static final String OK = "OK";
public static final String ERROR = "Error";
+ public static final String LOADING = "Loading";
public static final String WARNING = "Warning";
public static final String NOT_SIGNED_IN = "NotSignedIn";
public static final String NOT_AUTHORIZED = "NotAuthorized";
@@ -57,6 +58,10 @@ public boolean isError() {
public boolean isNotAuthorized() {
return NOT_AUTHORIZED.equals(this.status);
}
+
+ public boolean isLoading() {
+ return LOADING.equals(this.status);
+ }
@Override
public int hashCode() {
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NullParams.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NullParams.java
new file mode 100644
index 00000000..4c6f4383
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NullParams.java
@@ -0,0 +1,8 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+/**
+ * Null parameters.
+ */
+public class NullParams {
+
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/SignInConfirmParams.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/SignInConfirmParams.java
new file mode 100644
index 00000000..c7552cec
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/SignInConfirmParams.java
@@ -0,0 +1,56 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Objects;
+
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * Parameter for SignInConfirm request.
+ */
+public class SignInConfirmParams {
+ @NonNull
+ public String userCode;
+
+ /**
+ * Create a new parameter for SignInConfirm request.
+ */
+ public SignInConfirmParams(String userCode) {
+ this.userCode = userCode;
+ }
+
+ public String getUserCode() {
+ return userCode;
+ }
+
+ public void setUserCode(String userCode) {
+ this.userCode = userCode;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(userCode);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ SignInConfirmParams other = (SignInConfirmParams) obj;
+ return Objects.equals(userCode, other.userCode);
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("userCode", userCode);
+ return builder.toString();
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/SignInInitiateResult.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/SignInInitiateResult.java
new file mode 100644
index 00000000..c78a9150
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/SignInInitiateResult.java
@@ -0,0 +1,102 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Objects;
+
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+
+/**
+ * Result for signInInitiate.
+ */
+public class SignInInitiateResult {
+
+ public static final String ALREADY_SIGNED_IN = "AlreadySignedIn";
+
+ private String status;
+ private Integer expiresIn;
+ private Integer interval;
+ private String userCode;
+ private String verificationUri;
+
+ /**
+ * Create a new SignInInitiateResult.
+ */
+ public SignInInitiateResult() {
+ }
+
+ public boolean isAlreadySignedIn() {
+ return ALREADY_SIGNED_IN.equals(status);
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public Integer getExpiresIn() {
+ return expiresIn;
+ }
+
+ public void setExpiresIn(Integer expiresIn) {
+ this.expiresIn = expiresIn;
+ }
+
+ public Integer getInterval() {
+ return interval;
+ }
+
+ public void setInterval(Integer interval) {
+ this.interval = interval;
+ }
+
+ public String getUserCode() {
+ return userCode;
+ }
+
+ public void setUserCode(String userCode) {
+ this.userCode = userCode;
+ }
+
+ public String getVerificationUri() {
+ return verificationUri;
+ }
+
+ public void setVerificationUri(String verificationUri) {
+ this.verificationUri = verificationUri;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(expiresIn, interval, status, userCode, verificationUri);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ SignInInitiateResult other = (SignInInitiateResult) obj;
+ return Objects.equals(expiresIn, other.expiresIn) && Objects.equals(interval, other.interval)
+ && Objects.equals(status, other.status) && Objects.equals(userCode, other.userCode)
+ && Objects.equals(verificationUri, other.verificationUri);
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("status", status);
+ builder.add("expiresIn", expiresIn);
+ builder.add("interval", interval);
+ builder.add("userCode", userCode);
+ builder.add("verificationUri", verificationUri);
+ return builder.toString();
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/utils/PlatformUtils.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/utils/PlatformUtils.java
index 9168986e..30277143 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/utils/PlatformUtils.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/utils/PlatformUtils.java
@@ -24,6 +24,22 @@ public static String getEclipseVersion() {
return bundle.getVersion().toString();
}
+ /**
+ * Escapes spaces in a URL string.
+ */
+ public static String escapeSpaceInUrl(String urlString) {
+ char[] chars = urlString.toCharArray();
+ StringBuffer sb = new StringBuffer(chars.length);
+ for (int i = 0; i < chars.length; i++) {
+ if (chars[i] == ' ') {
+ sb.append("%20");
+ } else {
+ sb.append(chars[i]);
+ }
+ }
+ return sb.toString();
+ }
+
public static boolean isMac() {
return Platform.getOS().equals(Platform.OS_MACOSX);
}
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/i18n/MessagesTest.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/i18n/MessagesTest.java
index 38a9e62b..e5314298 100644
--- a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/i18n/MessagesTest.java
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/i18n/MessagesTest.java
@@ -9,7 +9,7 @@ class MessagesTest {
@Test
void testMessagesInitialization() {
// Ensure that the static fields are initialized
- assertNotNull(Messages.INFO_signToGitHub);
- assertNotNull(Messages.INFO_signOutFromGitHub);
+ assertNotNull(Messages.menu_signToGitHub);
+ assertNotNull(Messages.menu_signOutFromGitHub);
}
}
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
index 267e163b..be667e90 100644
--- a/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
@@ -5,8 +5,10 @@ Bundle-SymbolicName: com.microsoft.copilot.eclipse.ui;singleton:=true
Bundle-Version: 0.1.0.qualifier
Export-Package: com.microsoft.copilot.eclipse.ui,
com.microsoft.copilot.eclipse.ui.completion,
+ com.microsoft.copilot.eclipse.ui.dialogs,
com.microsoft.copilot.eclipse.ui.handlers,
- com.microsoft.copilot.eclipse.ui.i18n
+ com.microsoft.copilot.eclipse.ui.i18n,
+ com.microsoft.copilot.eclipse.ui.utils
Bundle-Activator: com.microsoft.copilot.eclipse.ui.CopilotUi
Bundle-RequiredExecutionEnvironment: JavaSE-17
Automatic-Module-Name: com.microsoft.copilot.eclipse.ui
diff --git a/com.microsoft.copilot.eclipse.ui/plugin.properties b/com.microsoft.copilot.eclipse.ui/plugin.properties
index f2b1b840..4aadecf3 100644
--- a/com.microsoft.copilot.eclipse.ui/plugin.properties
+++ b/com.microsoft.copilot.eclipse.ui/plugin.properties
@@ -1,3 +1,6 @@
command.triggerInlineSuggestions.name=Trigger Inline Suggestion
command.acceptFullSuggestion.name=Accept Suggestion
command.discardSuggestion.name=Discard Suggestion
+command.copilotForEclipsePlugin.name=GitHub Copilot for Eclipse
+command.signInToGitHub.name=Sign in to GitHub Copilot
+command.signOutFromGitHub.name=Sign out from GitHub Copilot
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/plugin.xml b/com.microsoft.copilot.eclipse.ui/plugin.xml
index 0e341bfd..50a70a5a 100644
--- a/com.microsoft.copilot.eclipse.ui/plugin.xml
+++ b/com.microsoft.copilot.eclipse.ui/plugin.xml
@@ -28,26 +28,17 @@
-
-
-
-
-
+ id="com.microsoft.copilot.eclipse.commands.showStatusBarMenu"
+ name="%command.copilotForEclipsePlugin.name">
+
+
+
+
+
@@ -69,6 +60,14 @@
class="com.microsoft.copilot.eclipse.ui.handlers.ShowStatusBarMenuHandler"
commandId="com.microsoft.copilot.eclipse.commands.showStatusBarMenu">
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
new file mode 100644
index 00000000..998ca5f5
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
@@ -0,0 +1,133 @@
+package com.microsoft.copilot.eclipse.ui.dialogs;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.widgets.Shell;
+
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
+import com.microsoft.copilot.eclipse.ui.i18n.Messages;
+
+/**
+ * Dialog for confirming sign-in to GitHub Copilot.
+ */
+public class SignInConfirmDialog extends ProgressMonitorDialog {
+
+ private final String userCode;
+ private final long timeout;
+ private CompletableFuture future;
+ private IStatus status;
+
+ /**
+ * Constructs a new SignInConfirmDialog.
+ *
+ * @param parent the parent shell
+ * @param userCode the user code for sign-in confirmation
+ * @param timeout the timeout duration in milliseconds
+ */
+ public SignInConfirmDialog(Shell parent, String userCode, long timeout) {
+ super(parent);
+ this.userCode = userCode;
+ this.timeout = timeout;
+ this.future = null;
+ this.setCancelable(true);
+ }
+
+ @Override
+ protected void configureShell(Shell shell) {
+ super.configureShell(shell);
+ shell.setText(Messages.signInDialog_title);
+ }
+
+ /**
+ * Runs the sign-in confirmation process.
+ */
+ public void run() {
+ IRunnableWithProgress task = new SignInConfirmationTask();
+ try {
+ this.run(true, true, task);
+ } catch (Exception e) {
+ // TODO: log & send telemetry
+ }
+ }
+
+ /**
+ * Gets the status of the sign-in confirmation.
+ *
+ * @return the status of the sign-in confirmation
+ */
+ public IStatus getStatus() {
+ return this.status;
+ }
+
+ private class SignInConfirmationTask implements IRunnableWithProgress {
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ try {
+ future = CompletableFuture.supplyAsync(() -> {
+ try {
+ return CopilotCore.getPlugin().getAuthStatusManager().signInConfirm(userCode);
+ } catch (Exception e) {
+ // TODO: log & send telemetry
+ return null;
+ }
+ });
+
+ monitor.beginTask(Messages.signInConfirmDialog_progress, (int) timeout / 250);
+
+ waitForAuthorization(monitor);
+
+ if (future.isDone()) {
+ handleAuthorizationResult();
+ } else {
+ future.cancel(true);
+ status = Status.error(Messages.signInConfirmDialog_progressTimeout);
+ }
+ } catch (ExecutionException | InterruptedException e) {
+ status = Status.error(Messages.signInConfirmDialog_progressCanceled);
+ }
+ }
+
+ private void waitForAuthorization(IProgressMonitor monitor) throws InterruptedException {
+ int step = 250;
+ int steps = (int) timeout / step;
+
+ for (int i = 0; i < steps; i++) {
+ Thread.sleep(step);
+
+ if (monitor.isCanceled()) {
+ future.cancel(true);
+ return;
+ }
+
+ if (future.isDone()) {
+ break;
+ }
+
+ monitor.worked(1);
+ }
+
+ monitor.done();
+ }
+
+ private void handleAuthorizationResult() throws ExecutionException, InterruptedException {
+ AuthStatusResult result = future.get();
+ String errorMsg = null;
+
+ if (result == null || !result.isSignedIn()) {
+ errorMsg = Messages.signInConfirmDialog_authResult_notSignedIn;
+ } else if (result.isNotAuthorized()) {
+ errorMsg = Messages.signInConfirmDialog_authResult_notAuthed;
+ }
+
+ status = errorMsg != null ? Status.error(errorMsg) : Status.OK_STATUS;
+ }
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInDialog.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInDialog.java
new file mode 100644
index 00000000..38e100b3
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInDialog.java
@@ -0,0 +1,100 @@
+package com.microsoft.copilot.eclipse.ui.dialogs;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import com.microsoft.copilot.eclipse.core.lsp.protocol.SignInInitiateResult;
+import com.microsoft.copilot.eclipse.ui.i18n.Messages;
+import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
+import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
+
+/**
+ * Dialog for signing in to GitHub Copilot.
+ */
+public class SignInDialog extends MessageDialog {
+
+ private final SignInInitiateResult signInInitiateResult;
+
+ /**
+ * Constructs a new SignInDialog.
+ *
+ * @param parentShell the parent shell
+ * @param initResult the sign-in initiation result
+ */
+ public SignInDialog(Shell parentShell, SignInInitiateResult initResult) {
+ super(parentShell, Messages.signInDialog_title, null, null, MessageDialog.INFORMATION,
+ new String[] { Messages.signInDialog_button_cancel, Messages.signInDialog_button_copyOpen }, 1);
+ this.signInInitiateResult = initResult;
+ }
+
+ @Override
+ protected Control createCustomArea(Composite parent) {
+ Composite composite = createComposite(parent);
+ createDeviceCodeSection(composite);
+ createWebsiteSection(composite);
+ return composite;
+ }
+
+ private Composite createComposite(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true));
+ composite.setLayout(new GridLayout(2, false));
+ return composite;
+ }
+
+ private void createDeviceCodeSection(Composite composite) {
+ Label deviceCodeLabel = new Label(composite, SWT.NONE);
+ deviceCodeLabel.setText(Messages.signInDialog_info_deviceCodePrefix);
+ Text deviceCodeText = new Text(composite, SWT.SINGLE | SWT.READ_ONLY);
+ deviceCodeText.setText(this.signInInitiateResult.getUserCode());
+ deviceCodeText.setCursor(composite.getDisplay().getSystemCursor(SWT.CURSOR_ARROW));
+ deviceCodeText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ }
+
+ private void createWebsiteSection(Composite composite) {
+ Label websiteLabel = new Label(composite, SWT.NONE);
+ websiteLabel.setText(Messages.signInDialog_info_gitHubWebSitePrefix);
+ Link websiteLink = new Link(composite, SWT.NONE);
+ websiteLink.setText("" + this.signInInitiateResult.getVerificationUri() + "");
+ websiteLink.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ UiUtils.openLink(signInInitiateResult.getVerificationUri());
+ }
+ });
+ }
+
+ @Override
+ protected Control createMessageArea(Composite composite) {
+ this.message = Messages.signInDialog_info_instructions;
+ return super.createMessageArea(composite);
+ }
+
+ @Override
+ protected void buttonPressed(int buttonId) {
+ if (buttonId == 1) {
+ copyCodeToClipboard();
+ }
+ super.buttonPressed(buttonId);
+ }
+
+ private void copyCodeToClipboard() {
+ Clipboard clipboard = new Clipboard(SwtUtils.getDisplay());
+ TextTransfer textTransfer = TextTransfer.getInstance();
+ clipboard.setContents(new Object[] { this.signInInitiateResult.getUserCode() }, new Transfer[] { textTransfer });
+ clipboard.dispose();
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
index d94cdace..acf67332 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
@@ -5,12 +5,17 @@
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.eclipse.ui.handlers.IHandlerService;
-import com.microsoft.copilot.eclipse.ui.UiConstants;
+import com.microsoft.copilot.eclipse.core.AuthStatusManager;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
import com.microsoft.copilot.eclipse.ui.i18n.Messages;
import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
@@ -19,38 +24,85 @@
*/
public class ShowStatusBarMenuHandler extends AbstractHandler {
- /**
- * Render the status bar menu based on the logged-in state.
- */
+ private IHandlerService handlerService;
+ private ImageDescriptor icon;
+ private AuthStatusManager authStatusManager;
+
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
-
- Shell shell = PlatformUI.getWorkbench().getDisplay().getActiveShell();
+ handlerService = HandlerUtil.getActiveWorkbenchWindow(event).getService(IHandlerService.class);
+ authStatusManager = CopilotCore.getPlugin().getAuthStatusManager();
+ icon = ImageDescriptor.createFromURL(UiUtils.class.getResource("/icons/copilot.png"));
MenuManager menuManager = new MenuManager();
- ImageDescriptor icon = UiUtils.resizeIcon("/icons/copilot.png", UiConstants.TOOLBAR_ICON_WIDTH_IN_PIEXL,
- UiConstants.TOOLBAR_ICON_HEIGHT_IN_PIEXL);
-
- // TODO: Add GitHub sign-in states to the menu
- Action signInAction = new Action(Messages.INFO_signToGitHub, icon) {
- @Override
- public void run() {
- // Handle sign-in action
- }
- };
-
- // TODO: Add GitHub sign-out states to the menu
- Action signOutAction = new Action(Messages.INFO_signOutFromGitHub, icon) {
- @Override
- public void run() {
- // Handle sign-out action
- }
- };
-
- menuManager.add(signInAction);
- menuManager.add(signOutAction);
+ addStatusAction(menuManager);
+
+ if (!authStatusManager.getAuthStatusResult().isLoading()) {
+ menuManager.add(new Separator());
+ addSignInOrSignOutAction(menuManager);
+ }
+ Shell shell = PlatformUI.getWorkbench().getDisplay().getActiveShell();
Menu menu = menuManager.createContextMenu(shell);
menu.setVisible(true);
return null;
}
+
+ private void addStatusAction(MenuManager menuManager) {
+ String signInStatus = getSignInStatusBasedOnAuthResult(authStatusManager.getAuthStatusResult());
+ String signInStatusTitle = Messages.menu_signInStatus + ": " + signInStatus;
+
+ MenuActionFactory.createMenuAction(menuManager, signInStatusTitle, handlerService, signInStatus, false);
+ }
+
+ private String getSignInStatusBasedOnAuthResult(AuthStatusResult authStatusResult) {
+ switch (authStatusResult.getStatus()) {
+ case AuthStatusResult.OK:
+ return Messages.menu_signInStatus_ready;
+ case AuthStatusResult.ERROR:
+ return Messages.menu_signInStatus_unknownError;
+ case AuthStatusResult.LOADING:
+ return Messages.menu_signInStatus_loading;
+ case AuthStatusResult.NOT_SIGNED_IN:
+ return Messages.menu_signInStatus_notSignedInToGitHub;
+ case AuthStatusResult.WARNING:
+ return Messages.menu_signInStatus_agentWarning;
+ case AuthStatusResult.NOT_AUTHORIZED:
+ return Messages.menu_signInStatus_notAuthorized;
+ default:
+ return Messages.menu_signInStatus_loading;
+ }
+ }
+
+ private void addSignInOrSignOutAction(MenuManager menuManager) {
+ if (authStatusManager.getAuthStatusResult().isSignedIn()) {
+ MenuActionFactory.createMenuAction(menuManager, Messages.menu_signOutFromGitHub, icon, handlerService,
+ "com.microsoft.copilot.eclipse.commands.signOut", true);
+ } else {
+ MenuActionFactory.createMenuAction(menuManager, Messages.menu_signToGitHub, icon, handlerService,
+ "com.microsoft.copilot.eclipse.commands.signIn", true);
+ }
+ }
+
+ private static class MenuActionFactory {
+ public static void createMenuAction(MenuManager menuManager, String actionName, ImageDescriptor icon,
+ IHandlerService handlerService, String commandId, boolean enabled) {
+ Action action = new Action(actionName, icon) {
+ @Override
+ public void run() {
+ try {
+ handlerService.executeCommand(commandId, null);
+ } catch (Exception e) {
+ // TODO: log & send telemetry
+ }
+ }
+ };
+ action.setEnabled(enabled);
+ menuManager.add(action);
+ }
+
+ public static void createMenuAction(MenuManager menuManager, String text, IHandlerService handlerService,
+ String commandId, boolean enabled) {
+ createMenuAction(menuManager, text, null, handlerService, commandId, enabled);
+ }
+ }
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
new file mode 100644
index 00000000..0c85a367
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
@@ -0,0 +1,116 @@
+package com.microsoft.copilot.eclipse.ui.handlers;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Shell;
+
+import com.microsoft.copilot.eclipse.core.AuthStatusManager;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.SignInInitiateResult;
+import com.microsoft.copilot.eclipse.ui.dialogs.SignInConfirmDialog;
+import com.microsoft.copilot.eclipse.ui.dialogs.SignInDialog;
+import com.microsoft.copilot.eclipse.ui.i18n.Messages;
+import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
+import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
+
+/**
+ * Handler for signing to GitHub Copilot.
+ */
+public class SignInHandler extends AbstractHandler {
+
+ private static final long SIGNIN_TIMEOUT_MILLIS = 180000L;
+
+ private CopilotLanguageServerConnection languageServer;
+ private AuthStatusManager authStatusManager;
+
+ /**
+ * Initialize the Copilot Language Server for the SignInHandler.
+ */
+ public SignInHandler() {
+ this.languageServer = CopilotCore.getPlugin().getCopilotLanguageServer();
+ this.authStatusManager = CopilotCore.getPlugin().getAuthStatusManager();
+ }
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ Shell shell = SwtUtils.getShellFromEvent(event);
+
+ try {
+ SignInInitiateResult result = initiateSignIn();
+ if (result.isAlreadySignedIn()) {
+ showAlreadySignedInMessage(shell);
+ } else {
+ handleSignIn(shell, result);
+ }
+ } catch (Exception e) {
+ handleSignInException(shell, e);
+ // TODO log & send telemetry
+ }
+
+ return null;
+ }
+
+ private SignInInitiateResult initiateSignIn() throws Exception {
+ return this.authStatusManager.signInInitiate();
+ }
+
+ private void showAlreadySignedInMessage(Shell shell) {
+ MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_title,
+ Messages.signInHandler_msgDialog_alreadySignedIn);
+ }
+
+ private void handleSignIn(Shell shell, SignInInitiateResult result) {
+ AtomicReference signInInitiateResultHolder = new AtomicReference<>(result);
+ SwtUtils.invokeOnDisplayThread(() -> {
+ SignInDialog signInDialog = new SignInDialog(shell, signInInitiateResultHolder.get());
+ int btnId = signInDialog.open();
+ if (btnId > 0) {
+ UiUtils.openLink(signInInitiateResultHolder.get().getVerificationUri());
+ SignInConfirmDialog signInConfirmDialog = new SignInConfirmDialog(shell,
+ signInInitiateResultHolder.get().getUserCode(), SIGNIN_TIMEOUT_MILLIS);
+ signInConfirmDialog.run();
+ handleSignInConfirmation(shell, signInConfirmDialog);
+ }
+ });
+ }
+
+ private void handleSignInConfirmation(Shell shell, SignInConfirmDialog signInConfirmDialog) {
+ IStatus status = signInConfirmDialog.getStatus();
+ if (status != null && status.isOK()) {
+ showSignInSuccessMessage(shell);
+ } else {
+ showSignInFailMessage(shell, status);
+ }
+ }
+
+ private void showSignInSuccessMessage(Shell shell) {
+ MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_gitHubCopilot,
+ Messages.signInHandler_msgDialog_signInSuccess);
+ }
+
+ private void showSignInFailMessage(Shell shell, IStatus status) {
+ String msg = Messages.signInHandler_msgDialog_signInFailed;
+ if (status != null && StringUtils.isNotBlank(status.getMessage())) {
+ msg += ": " + status.getMessage();
+ }
+ msg += ". ";
+ MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_gitHubCopilot,
+ msg + Messages.signInHandler_msgDialog_signInFailedTryAgain);
+ }
+
+ private void handleSignInException(Shell shell, Exception e) {
+ String msg = Messages.signInHandler_msgDialog_signInFailed;
+ if (StringUtils.isNotBlank(e.getMessage())) {
+ msg += " " + e.getMessage();
+ // TODO log & send telemetry
+ }
+ MessageDialog.openError(shell, Messages.signInHandler_msgDialog_signInFailedFailure, msg);
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
new file mode 100644
index 00000000..0dd913d7
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
@@ -0,0 +1,64 @@
+package com.microsoft.copilot.eclipse.ui.handlers;
+
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Shell;
+
+import com.microsoft.copilot.eclipse.core.AuthStatusManager;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
+import com.microsoft.copilot.eclipse.ui.i18n.Messages;
+import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
+
+/**
+ * Handler for signing out from GitHub Copilot.
+ */
+public class SignOutHandler extends AbstractHandler {
+
+ private CopilotLanguageServerConnection languageServer;
+ private AuthStatusManager authStatusManager;
+
+ /**
+ * Initialize the Copilot Language Server and Auth Status Manager for the SignOutHandler.
+ */
+ public SignOutHandler() {
+ this.languageServer = CopilotCore.getPlugin().getCopilotLanguageServer();
+ this.authStatusManager = CopilotCore.getPlugin().getAuthStatusManager();
+ }
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ Shell shell = SwtUtils.getShellFromEvent(event);
+ try {
+ AuthStatusResult result = this.languageServer.signOut().get();
+ if (!result.isSignedIn()) {
+ showSignOutMessage(shell);
+ authStatusManager.checkStatus();
+ }
+ } catch (Exception e) {
+ handleSignOutException(shell, e);
+ // TODO: log & send telemetry
+ }
+
+ return null;
+ }
+
+ private void handleSignOutException(Shell shell, Exception e) {
+ String msg = Messages.signOutHandler_msgDialog_signOutFailed;
+ if (StringUtils.isNotBlank(e.getMessage())) {
+ msg += " " + e.getMessage();
+ // TODO: log & send telemetry
+ }
+ MessageDialog.openError(shell, Messages.signOutHandler_msgDialog_signOutFailedFailure, msg);
+ }
+
+ private void showSignOutMessage(Shell shell) {
+ MessageDialog.openInformation(shell, Messages.signOutHandler_msgDialog_gitHubCopilot,
+ Messages.signOutHandler_msgDialog_signOutSuccess);
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
index 94e0afaa..d17b897e 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
@@ -7,8 +7,38 @@
*/
public final class Messages extends NLS {
private static final String BUNDLE_NAME = "com.microsoft.copilot.eclipse.ui.i18n.messages"; //$NON-NLS-1$
- public static String INFO_signToGitHub;
- public static String INFO_signOutFromGitHub;
+ public static String menu_signInStatus;
+ public static String menu_signInStatus_ready;
+ public static String menu_signInStatus_loading;
+ public static String menu_signInStatus_notSignedInToGitHub;
+ public static String menu_signInStatus_unknownError;
+ public static String menu_signInStatus_notAuthorized;
+ public static String menu_signInStatus_agentWarning;
+ public static String menu_signToGitHub;
+ public static String menu_signOutFromGitHub;
+ public static String signInDialog_title;
+ public static String signInDialog_button_cancel;
+ public static String signInDialog_button_copyOpen;
+ public static String signInDialog_info_instructions;
+ public static String signInDialog_info_deviceCodePrefix;
+ public static String signInDialog_info_gitHubWebSitePrefix;
+ public static String signInConfirmDialog_progress;
+ public static String signInConfirmDialog_progressSuffix;
+ public static String signInConfirmDialog_progressTimeout;
+ public static String signInConfirmDialog_progressCanceled;
+ public static String signInConfirmDialog_authResult_notSignedIn;
+ public static String signInConfirmDialog_authResult_notAuthed;
+ public static String signInHandler_msgDialog_gitHubCopilot;
+ public static String signInHandler_msgDialog_title;
+ public static String signInHandler_msgDialog_alreadySignedIn;
+ public static String signInHandler_msgDialog_signInSuccess;
+ public static String signInHandler_msgDialog_signInFailed;
+ public static String signInHandler_msgDialog_signInFailedTryAgain;
+ public static String signInHandler_msgDialog_signInFailedFailure;
+ public static String signOutHandler_msgDialog_gitHubCopilot;
+ public static String signOutHandler_msgDialog_signOutSuccess;
+ public static String signOutHandler_msgDialog_signOutFailed;
+ public static String signOutHandler_msgDialog_signOutFailedFailure;
static {
// initialize resource bundle
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
index ae3f5feb..50672d46 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
@@ -1,2 +1,32 @@
-INFO_signToGitHub=Sign In to GitHub
-INFO_signOutFromGitHub=Sign Out from GitHub
+menu_signInStatus=Status
+menu_signInStatus_ready=Ready
+menu_signInStatus_loading=Loading
+menu_signInStatus_notSignedInToGitHub=Not signed in to GitHub
+menu_signInStatus_unknownError=Unknown error
+menu_signInStatus_notAuthorized=No access to GitHub Copilot
+menu_signInStatus_agentWarning=Copilot is encountering temporary issues
+menu_signToGitHub=Sign In to GitHub
+menu_signOutFromGitHub=Sign Out from GitHub
+
+signInDialog_title=Sign In to GitHub
+signInDialog_button_cancel=Cancel
+signInDialog_button_copyOpen=Copy Code and Open
+signInDialog_info_instructions=GitHub Copilot uses a GitHub account. Please enter the following code on the GitHub website to authorize your GitHub account with GitHub Copilot.
+signInDialog_info_deviceCodePrefix=Device code:
+signInDialog_info_gitHubWebSitePrefix=GitHub website:
+signInConfirmDialog_progress=Waiting for GitHub Copilot authorization...
+signInConfirmDialog_progressTimeout=Authorization request failed: Process timed out.
+signInConfirmDialog_progressCanceled=Authorization request failed: process aborted.
+signInConfirmDialog_authResult_notSignedIn=Authorization request failed: Not signed in.
+signInConfirmDialog_authResult_notAuthed=Authorization request failed: Your subscription may be expired.
+signInHandler_msgDialog_gitHubCopilot=GitHub Copilot
+signInHandler_msgDialog_title=Sign In to GitHub
+signInHandler_msgDialog_alreadySignedIn=User already signed in.
+signInHandler_msgDialog_signInSuccess=You have successfully signed in and authorized GitHub Copilot access to your GitHub account.
+signInHandler_msgDialog_signInFailed=Unable to sign in to GitHub Copilot at this time
+signInHandler_msgDialog_signInFailedTryAgain= Please try again to resume use of GitHub Copilot features.
+signInHandler_msgDialog_signInFailedFailure=Copilot Sign In Failure
+signOutHandler_msgDialog_gitHubCopilot=GitHub Copilot
+signOutHandler_msgDialog_signOutSuccess=You have successfully signed out from Copilot.
+signOutHandler_msgDialog_signOutFailed=Unable to sign out to GitHub Copilot at this time
+signOutHandler_msgDialog_signOutFailedFailure=Copilot Sign Out Failure
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java
index b745f8ed..8bea0b49 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java
@@ -3,13 +3,17 @@
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
/**
* Utilities for SWT. *
@@ -68,4 +72,26 @@ public static IEditorPart getActiveEditorPart() {
});
return ref.get();
}
+
+ /**
+ * This method retrieves the active workbench window from the event and then gets the shell associated with that
+ * window. It is more specific to the Eclipse framework and is typically used in handlers for commands or actions
+ * within the Eclipse environment.
+
+ * @throws ExecutionException if the active workbench window cannot be retrieved from the event.
+ */
+ public static Shell getShellFromEvent(ExecutionEvent event) throws ExecutionException {
+ return HandlerUtil.getActiveWorkbenchWindowChecked(event).getShell();
+ }
+
+ /**
+ * Get current display.
+ */
+ public static Display getDisplay() {
+ Display display = Display.getCurrent();
+ if (display == null) {
+ display = Display.getDefault();
+ }
+ return display;
+ }
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
index 69d91632..ad306c53 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
@@ -1,7 +1,10 @@
package com.microsoft.copilot.eclipse.ui.utils;
+import java.net.URI;
import java.util.concurrent.atomic.AtomicInteger;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.swt.custom.StyledText;
@@ -9,6 +12,14 @@
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.browser.IWebBrowser;
+import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import com.microsoft.copilot.eclipse.core.utils.PlatformUtils;
/**
* Utilities for Eclipse UI.
@@ -19,6 +30,36 @@ private UiUtils() {
// prevent instantiation
}
+ /**
+ * Gets the URI of the file opened in the given text editor.
+ */
+ @Nullable
+ public static URI getUriFromTextEditor(ITextEditor editor) {
+ IEditorInput input = editor.getEditorInput();
+ if (input instanceof IFileEditorInput fileInput) {
+ IFile file = fileInput.getFile();
+ return file.getLocationURI();
+ }
+
+ return null;
+ }
+
+ /**
+ * Open the given link in a new browser page.
+ */
+ public static boolean openLink(String link) {
+ String encodedUrl = PlatformUtils.escapeSpaceInUrl(link);
+ IWorkbenchBrowserSupport browserSupport = PlatformUI.getWorkbench().getBrowserSupport();
+ try {
+ IWebBrowser browser = browserSupport.createBrowser(IWorkbenchBrowserSupport.AS_EXTERNAL, null, null, null);
+ browser.openURL(new URI(encodedUrl).toURL());
+ } catch (Exception e) {
+ // TODO: log & send telemetry
+ return false;
+ }
+ return true;
+ }
+
/**
* Resizes the icon at the given path to the given width and height. Icon size is 16x16 by default, which is the
* recommended size for toolbar icons. For more details: https://eclipse-platform.github.io/ui-best-practices/#toolbar
@@ -46,5 +87,4 @@ public static int getCaretOffset(ITextViewer textViewer) {
}, styledText);
return ref.get();
}
-
}
From e69036d3f3206e2d726548b52f5b4fc132830b53 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Wed, 18 Dec 2024 16:54:17 +0800
Subject: [PATCH 023/690] feat - Notify the completion result (#31)
* Three notification will be sent to CLS during completion: notifyShown,
notifyAccepted & notifyRejected.
---
.../completion/CompletionCollectionTests.java | 8 +++
.../core/completion/CompletionCollection.java | 6 +-
.../core/lsp/CopilotLanguageServer.java | 22 +++++-
.../lsp/CopilotLanguageServerConnection.java | 40 +++++++++--
.../lsp/protocol/NotifyAcceptedParams.java | 70 +++++++++++++++++++
.../lsp/protocol/NotifyRejectedParams.java | 59 ++++++++++++++++
.../core/lsp/protocol/NotifyShownParams.java | 58 +++++++++++++++
.../AcceptFullSuggestionHandlerTests.java | 40 +++++++++++
.../DiscardSuggestionHandlerTests.java | 39 +++++++++++
.../ui/completion/CompletionHandler.java | 5 ++
.../ui/completion/CompletionManager.java | 21 ++++++
.../handlers/AcceptFullSuggestionHandler.java | 15 ++++
.../eclipse/ui/handlers/CopilotHandler.java | 6 ++
.../ui/handlers/DiscardSuggestionHandler.java | 18 +++++
14 files changed, 401 insertions(+), 6 deletions(-)
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NotifyAcceptedParams.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NotifyRejectedParams.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NotifyShownParams.java
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollectionTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollectionTests.java
index 92b99a01..ca1f295e 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollectionTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollectionTests.java
@@ -60,4 +60,12 @@ void getNumberOfLinesReturnsCorrectNumberOfLines() {
assertEquals(3, collection.getNumberOfLines());
}
+ @Test
+ void testGetUuids() {
+ List completions = List.of(new CompletionItem("uuid1", "test", null, "displayText1", null, 0),
+ new CompletionItem("uuid2", "test", null, "displayText1", null, 0));
+ CompletionCollection collection = new CompletionCollection(completions, "uri");
+ assertEquals(List.of("uuid1", "uuid2"), collection.getUuids());
+ }
+
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollection.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollection.java
index 68ff19e0..e3a39533 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollection.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollection.java
@@ -93,10 +93,14 @@ public int getNumberOfLines() {
return this.getText().split("\n").length;
}
+ public List getUuids() {
+ return this.completions.stream().map(CompletionItem::getUuid).toList();
+ }
+
/**
* Get the current active completion item.
*/
- CompletionItem getCurrentItem() {
+ public CompletionItem getCurrentItem() {
return this.completions.get(index);
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java
index 1b214768..5368266c 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java
@@ -9,6 +9,9 @@
import com.microsoft.copilot.eclipse.core.lsp.protocol.CheckStatusParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyAcceptedParams;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyRejectedParams;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyShownParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NullParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.SignInConfirmParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.SignInInitiateResult;
@@ -35,7 +38,7 @@ public interface CopilotLanguageServer extends LanguageServer {
*/
@JsonRequest
CompletableFuture signInInitiate(NullParams param);
-
+
/**
* Confirm the sign in process.
*/
@@ -48,4 +51,21 @@ public interface CopilotLanguageServer extends LanguageServer {
@JsonRequest
CompletableFuture signOut(NullParams params);
+ /**
+ * Notify the language server that the completion was shown.
+ */
+ @JsonRequest
+ CompletableFuture notifyShown(NotifyShownParams params);
+
+ /**
+ * Notify the language server that the completion was accepted.
+ */
+ @JsonRequest
+ CompletableFuture notifyAccepted(NotifyAcceptedParams params);
+
+ /**
+ * Notify the language server that the completion was rejected.
+ */
+ @JsonRequest
+ CompletableFuture notifyRejected(NotifyRejectedParams params);
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
index 47c46be1..0a1e6f02 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
@@ -3,7 +3,6 @@
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.eclipse.jface.text.IDocument;
@@ -15,6 +14,9 @@
import com.microsoft.copilot.eclipse.core.lsp.protocol.CheckStatusParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyAcceptedParams;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyRejectedParams;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyShownParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NullParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.SignInConfirmParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.SignInInitiateResult;
@@ -82,7 +84,8 @@ public CompletableFuture getCompletions(CompletionParams param
}
/**
- * Please use the {@link AuthStatusManager#signInInitiate()} method instead.
+ * Please use the {@link AuthStatusManager#signInInitiate()} method instead.
+ *
* Initiate the sign in process.
*/
public CompletableFuture signInInitiate() {
@@ -92,7 +95,8 @@ public CompletableFuture signInInitiate() {
}
/**
- * Please use the {@link AuthStatusManager#signInConfirm()} method instead.
+ * Please use the {@link AuthStatusManager#signInConfirm()} method instead.
+ *
* Confirm the sign in process.
*/
public CompletableFuture signInConfirm(String userCode) {
@@ -104,7 +108,8 @@ public CompletableFuture signInConfirm(String userCode) {
}
/**
- * Please use the {@link AuthStatusManager#signOut()} method instead.
+ * Please use the {@link AuthStatusManager#signOut()} method instead.
+ *
* Sign out from the GitHub Copilot.
*/
public CompletableFuture signOut() {
@@ -113,6 +118,33 @@ public CompletableFuture signOut() {
return this.languageServerWrapper.execute(fn);
}
+ /**
+ * Notify the language server that the completion was shown.
+ */
+ public CompletableFuture notifyShown(NotifyShownParams params) {
+ Function> fn = server -> ((CopilotLanguageServer) server)
+ .notifyShown(params);
+ return this.languageServerWrapper.execute(fn);
+ }
+
+ /**
+ * Notify the language server that the completion was accepted.
+ */
+ public CompletableFuture notifyAccepted(NotifyAcceptedParams params) {
+ Function> fn = server -> ((CopilotLanguageServer) server)
+ .notifyAccepted(params);
+ return this.languageServerWrapper.execute(fn);
+ }
+
+ /**
+ * Notify the language server that the completion was rejected.
+ */
+ public CompletableFuture notifyRejected(NotifyRejectedParams params) {
+ Function> fn = server -> ((CopilotLanguageServer) server)
+ .notifyRejected(params);
+ return this.languageServerWrapper.execute(fn);
+ }
+
/**
* Stop the language server.
*/
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NotifyAcceptedParams.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NotifyAcceptedParams.java
new file mode 100644
index 00000000..b6f77035
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NotifyAcceptedParams.java
@@ -0,0 +1,70 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Objects;
+
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * Parameter used for the notify a completion acceptance.
+ */
+public class NotifyAcceptedParams {
+
+ @NonNull
+ private String uuid;
+
+ private int acceptedLength;
+
+ /**
+ * Create a new NotifyAcceptedParams.
+ */
+ public NotifyAcceptedParams(String uuid) {
+ super();
+ this.uuid = uuid;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public int getAcceptedLength() {
+ return acceptedLength;
+ }
+
+ public void setAcceptedLength(int acceptedLength) {
+ this.acceptedLength = acceptedLength;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(acceptedLength, uuid);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ NotifyAcceptedParams other = (NotifyAcceptedParams) obj;
+ return acceptedLength == other.acceptedLength && Objects.equals(uuid, other.uuid);
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("uuid", uuid);
+ builder.add("acceptedLength", acceptedLength);
+ return builder.toString();
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NotifyRejectedParams.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NotifyRejectedParams.java
new file mode 100644
index 00000000..08146950
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NotifyRejectedParams.java
@@ -0,0 +1,59 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * Parameter used for the notify a completion rejection.
+ */
+public class NotifyRejectedParams {
+
+ @NonNull
+ private List uuids;
+
+ /**
+ * Create a new NotifyRejectedParams.
+ */
+ public NotifyRejectedParams(List uuids) {
+ this.uuids = uuids;
+ }
+
+ public List getUuids() {
+ return uuids;
+ }
+
+ public void setUuids(List uuids) {
+ this.uuids = uuids;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuids);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ NotifyRejectedParams other = (NotifyRejectedParams) obj;
+ return Objects.equals(uuids, other.uuids);
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("uuids", uuids);
+ return builder.toString();
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NotifyShownParams.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NotifyShownParams.java
new file mode 100644
index 00000000..37d8e7bc
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/NotifyShownParams.java
@@ -0,0 +1,58 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Objects;
+
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+
+/**
+ * * Parameters for the notifyShown request.
+ */
+public class NotifyShownParams {
+
+ @NonNull
+ private String uuid;
+
+ /**
+ * Creates a new NotifyShownParams.
+ */
+ public NotifyShownParams(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuid);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ NotifyShownParams other = (NotifyShownParams) obj;
+ return Objects.equals(uuid, other.uuid);
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("uuid", uuid);
+ return builder.toString();
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/AcceptFullSuggestionHandlerTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/AcceptFullSuggestionHandlerTests.java
index eb57d598..ebf06b18 100644
--- a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/AcceptFullSuggestionHandlerTests.java
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/AcceptFullSuggestionHandlerTests.java
@@ -1,15 +1,25 @@
package com.microsoft.copilot.eclipse.ui.handler;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import java.util.List;
+
+import org.eclipse.core.commands.ExecutionException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
@@ -35,4 +45,34 @@ void testIsNotEnabledWhenNoCompletionIsAvailable() {
}
}
+ @Test
+ void testAcceptionNotifiedWhenCompletionIsAccepted() throws ExecutionException {
+ CopilotLanguageServerConnection mockedConnection = mock(CopilotLanguageServerConnection.class);
+ when(mockedConnection.notifyAccepted(any())).thenReturn(null);
+ CopilotCore mockedCore = mock(CopilotCore.class);
+ when(mockedCore.getCopilotLanguageServer()).thenReturn(mockedConnection);
+
+ CompletionCollection completions = new CompletionCollection(
+ List.of(new CompletionItem("uuid", "text", null, "displayText", null, 0)), "uri");
+ CompletionHandler mockedHandler = mock(CompletionHandler.class);
+ doNothing().when(mockedHandler).acceptFullSuggestion();
+ when(mockedHandler.getCompletions()).thenReturn(completions);
+ EditorsManager mockedManager = mock(EditorsManager.class);
+ when(mockedManager.getActiveCompletionHandler()).thenReturn(mockedHandler);
+ CopilotUi mockedUi = mock(CopilotUi.class);
+ when(mockedUi.getEditorsManager()).thenReturn(mockedManager);
+
+ AcceptFullSuggestionHandler handler = new AcceptFullSuggestionHandler();
+
+ try (MockedStatic mockedStatic = mockStatic(CopilotUi.class);
+ MockedStatic mockedStaticCore = mockStatic(CopilotCore.class)) {
+ mockedStatic.when(CopilotUi::getPlugin).thenReturn(mockedUi);
+ mockedStaticCore.when(CopilotCore::getPlugin).thenReturn(mockedCore);
+
+ handler.execute(null);
+
+ verify(mockedConnection).notifyAccepted(any());
+ }
+ }
+
}
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/DiscardSuggestionHandlerTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/DiscardSuggestionHandlerTests.java
index 1d9d8be3..97061c0c 100644
--- a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/DiscardSuggestionHandlerTests.java
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/DiscardSuggestionHandlerTests.java
@@ -1,15 +1,24 @@
package com.microsoft.copilot.eclipse.ui.handler;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import java.util.List;
+
+import org.eclipse.core.commands.ExecutionException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
@@ -35,4 +44,34 @@ void testIsNotEnabledWhenNoCompletionIsAvailable() {
}
}
+ @Test
+ void testRejectionNotifiedWhenCompletionIsDiscarded() throws ExecutionException {
+ CopilotLanguageServerConnection mockedConnection = mock(CopilotLanguageServerConnection.class);
+ when(mockedConnection.notifyRejected(any())).thenReturn(null);
+ CopilotCore mockedCore = mock(CopilotCore.class);
+ when(mockedCore.getCopilotLanguageServer()).thenReturn(mockedConnection);
+
+ CompletionCollection completions = mock(CompletionCollection.class);
+ when(completions.getUuids()).thenReturn(List.of("uuid"));
+ CompletionHandler mockedHandler = mock(CompletionHandler.class);
+ doNothing().when(mockedHandler).clearCompletionRendering();
+ when(mockedHandler.getCompletions()).thenReturn(completions);
+ EditorsManager mockedManager = mock(EditorsManager.class);
+ when(mockedManager.getActiveCompletionHandler()).thenReturn(mockedHandler);
+ CopilotUi mockedUi = mock(CopilotUi.class);
+ when(mockedUi.getEditorsManager()).thenReturn(mockedManager);
+
+ DiscardSuggestionHandler handler = new DiscardSuggestionHandler();
+
+ try (MockedStatic mockedStatic = mockStatic(CopilotUi.class);
+ MockedStatic mockedStaticCore = mockStatic(CopilotCore.class)) {
+ mockedStatic.when(CopilotUi::getPlugin).thenReturn(mockedUi);
+ mockedStaticCore.when(CopilotCore::getPlugin).thenReturn(mockedCore);
+
+ handler.execute(null);
+
+ verify(mockedConnection).notifyRejected(any());
+ }
+ }
+
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
index 825a3c51..367dff8b 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
@@ -17,6 +17,7 @@
import org.eclipse.swt.custom.CaretListener;
import org.eclipse.ui.texteditor.ITextEditor;
+import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
@@ -130,6 +131,10 @@ public void clearCompletionRendering() {
this.completionManager.clearGhostText(this.triggerPosition);
}
+ public CompletionCollection getCompletions() {
+ return this.completionManager.getCompletions();
+ }
+
@Override
public void caretMoved(CaretEvent event) {
// it's guaranteed that the document change event comes earlier than caret
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
index bcb9fb50..82026941 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
@@ -22,6 +22,8 @@
import com.microsoft.copilot.eclipse.core.completion.CompletionListener;
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyShownParams;
import com.microsoft.copilot.eclipse.ui.UiConstants;
import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
@@ -104,6 +106,7 @@ public void onCompletionResolved(CompletionCollection completions) {
StyledText styledText = textViewer.getTextWidget();
if (styledText != null) {
SwtUtils.invokeOnDisplayThread(styledText::redraw, styledText);
+ this.notifyShown();
}
}
@@ -185,6 +188,10 @@ public void acceptSuggestion() throws BadLocationException {
this.document.replace(offset, 0, text);
}
+ public CompletionCollection getCompletions() {
+ return completions;
+ }
+
/**
* Dispose the resources used by the completion manager.
*/
@@ -197,4 +204,18 @@ public void dispose() {
}
}
+ private void notifyShown() {
+ if (this.completions == null || this.completions.getSize() == 0) {
+ return;
+ }
+
+ CompletionItem item = this.completions.getCurrentItem();
+ if (item == null) {
+ return;
+ }
+
+ NotifyShownParams params = new NotifyShownParams(item.getUuid());
+ this.lsConnection.notifyShown(params);
+ }
+
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/AcceptFullSuggestionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/AcceptFullSuggestionHandler.java
index 52e31a46..ed356dd9 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/AcceptFullSuggestionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/AcceptFullSuggestionHandler.java
@@ -3,6 +3,9 @@
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
+import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyAcceptedParams;
import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
/**
@@ -14,6 +17,7 @@ public class AcceptFullSuggestionHandler extends CopilotHandler {
public Object execute(ExecutionEvent event) throws ExecutionException {
CompletionHandler handler = getActiveCompletionHandler();
if (handler != null) {
+ notifyAccepted(handler.getCompletions());
handler.acceptFullSuggestion();
}
return null;
@@ -27,4 +31,15 @@ public boolean isEnabled() {
}
return false;
}
+
+ private void notifyAccepted(CompletionCollection completions) {
+ if (completions == null || completions.getSize() == 0) {
+ return;
+ }
+
+ CompletionItem item = completions.getCurrentItem();
+ String uuid = item.getUuid();
+ NotifyAcceptedParams params = new NotifyAcceptedParams(uuid);
+ getLanguageServerConnection().notifyAccepted(params);
+ }
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java
index 913fbbb8..61070cc2 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java
@@ -3,6 +3,8 @@
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.jdt.annotation.Nullable;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
@@ -26,4 +28,8 @@ public CompletionHandler getActiveCompletionHandler() {
}
return manager.getActiveCompletionHandler();
}
+
+ public CopilotLanguageServerConnection getLanguageServerConnection() {
+ return CopilotCore.getPlugin().getCopilotLanguageServer();
+ }
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/DiscardSuggestionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/DiscardSuggestionHandler.java
index aa46caf8..2eba32cf 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/DiscardSuggestionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/DiscardSuggestionHandler.java
@@ -1,8 +1,12 @@
package com.microsoft.copilot.eclipse.ui.handlers;
+import java.util.List;
+
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
+import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyRejectedParams;
import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
/**
@@ -13,6 +17,7 @@ public class DiscardSuggestionHandler extends CopilotHandler {
public Object execute(ExecutionEvent event) throws ExecutionException {
CompletionHandler handler = getActiveCompletionHandler();
if (handler != null) {
+ notifyRejected(handler.getCompletions());
handler.clearCompletionRendering();
}
return null;
@@ -26,4 +31,17 @@ public boolean isEnabled() {
}
return false;
}
+
+ private void notifyRejected(CompletionCollection completions) {
+ if (completions == null) {
+ return;
+ }
+ List uuids = completions.getUuids();
+ if (uuids == null || uuids.isEmpty()) {
+ return;
+ }
+
+ NotifyRejectedParams params = new NotifyRejectedParams(uuids);
+ getLanguageServerConnection().notifyRejected(params);
+ }
}
From 2a3809c6dbf7bd782079d5c88a878e9e6d8b6944 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Thu, 19 Dec 2024 16:23:48 +0800
Subject: [PATCH 024/690] fix - Index out of bound when drawing ghost text
(#35)
* fix - Index out of bound when drawing ghost text
* The bug is because the offset in the model (IDocument) and in the widget
(StyledText) can be different. Especially this will happen when some of
the code are folded in the UI (like imports). So we need to convert the model
offset to widget offset when we are drawing the ghost text.
* Address comments
---
.../eclipse/core/AuthStatusManagerTests.java | 5 ++--
.../ui/completion/CompletionHandler.java | 10 +++++--
.../ui/completion/CompletionManager.java | 21 ++++++++------
.../completion/EditorLifecycleListener.java | 29 +++++++++++--------
.../copilot/eclipse/ui/utils/SwtUtils.java | 4 +--
.../copilot/eclipse/ui/utils/UiUtils.java | 22 ++++++++------
target-platform.target | 28 +++++++-----------
7 files changed, 63 insertions(+), 56 deletions(-)
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
index 416e1829..96d5f2d0 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
@@ -37,10 +37,10 @@ void testAuthStatusResultOnSuccess() {
assertEquals(AuthStatusResult.OK, authStatusManager.getAuthStatusResult().getStatus());
}
-
+
@Test
void testCheckStatusLoadingWithDelay() throws InterruptedException {
- String mockedUser = "mockedUser";
+ String mockedUser = "mockedUser";
// Arrange
AuthStatusResult expectedResult = new AuthStatusResult();
expectedResult.setStatus(AuthStatusResult.OK);
@@ -55,7 +55,6 @@ void testCheckStatusLoadingWithDelay() throws InterruptedException {
// Assert initial status is LOADING
assertEquals(AuthStatusResult.LOADING, authStatusManager.getAuthStatusResult().getStatus());
-
future.complete(expectedResult);
// Assert final status is OK
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
index 367dff8b..9eac539e 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
@@ -137,6 +137,9 @@ public CompletionCollection getCompletions() {
@Override
public void caretMoved(CaretEvent event) {
+ int caretOffset = UiUtils.getCaretOffset(this.textViewer);
+ this.triggerPosition = new org.eclipse.jface.text.Position(caretOffset);
+
// it's guaranteed that the document change event comes earlier than caret
// change event. See org.eclipse.swt.custom.StyledText#modifyContent()
int currentVersion = this.lsConnection.getDocumentVersion(this.documentUri);
@@ -152,8 +155,6 @@ public void caretMoved(CaretEvent event) {
clearCompletionRendering();
} else {
this.documentVersion = currentVersion;
- int caretOffset = UiUtils.getCaretOffset(this.textViewer);
- this.triggerPosition = new org.eclipse.jface.text.Position(caretOffset);
triggerCompletion();
}
@@ -178,7 +179,10 @@ private String getCategory() {
* Disposes the resources of this completion handler.
*/
public void dispose() {
- this.completionManager.dispose();
+ if (this.completionManager != null) {
+ this.completionManager.dispose();
+ this.completionManager = null;
+ }
lsConnection.disconnectDocument(this.documentUri);
try {
this.document.removePositionCategory(this.getCategory());
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
index 82026941..e2e6d787 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
@@ -24,8 +24,10 @@
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyShownParams;
+import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.UiConstants;
import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
+import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
/**
* A class to control completion rendering.
@@ -75,6 +77,7 @@ public void triggerCompletion(Position position, int documentVersion) {
this.provider.triggerCompletion(documentUri.toASCIIString(),
LSPEclipseUtils.toPosition(position.getOffset(), this.document), documentVersion);
} catch (BadLocationException e) {
+ CopilotUi.getPlugin().getLog().info("triggerCompletion BadLocationException e 77");
// TODO log & send telemetry
}
}
@@ -118,20 +121,20 @@ public void paintControl(PaintEvent e) {
}
GC gc = e.gc;
- setLineVerticalIndentation(styledText, gc);
+ int widgetOffset = UiUtils.modelOffset2WidgetOffset(textViewer, this.triggerPosition.getOffset());
+ // will get index out of bounds if the cursor is at the end.
+ // Because there is no more text to get bounds at EOF.
+ widgetOffset = Math.max(Math.min(widgetOffset, styledText.getCharCount() - 1), 0);
+ setLineVerticalIndentation(styledText, gc, widgetOffset);
if (this.completions == null) {
return;
}
gc.setForeground(this.ghostTextColor);
- // will get index out of bounds if the cursor is at the end.
- // Because there is no more text to get bounds at EOF.
- int caretOffset = Math.min(this.triggerPosition.getOffset(), styledText.getCharCount() - 1);
-
String firstLine = this.completions.getFirstLine();
if (StringUtils.isNotBlank(firstLine)) {
- Rectangle bounds = styledText.getTextBounds(caretOffset, caretOffset);
+ Rectangle bounds = styledText.getTextBounds(widgetOffset, widgetOffset);
int y = bounds.y;
y += bounds.height - styledText.getLineHeight();
gc.drawString(firstLine, bounds.x + bounds.width, y, true);
@@ -141,14 +144,14 @@ public void paintControl(PaintEvent e) {
int lineHeight = styledText.getLineHeight();
int fontHeightt = gc.getFontMetrics().getHeight();
int x = styledText.getLeftMargin();
- Point offsetLocation = styledText.getLocationAtOffset(caretOffset);
+ Point offsetLocation = styledText.getLocationAtOffset(widgetOffset);
int y = offsetLocation.y + lineHeight * 2 - fontHeightt;
gc.drawText(remainingLines, x, y, true);
}
}
- private void setLineVerticalIndentation(StyledText styledText, GC gc) {
+ private void setLineVerticalIndentation(StyledText styledText, GC gc, int widgetOffset) {
int height = 0;
if (this.completions != null) {
// Change the height (line vertical indentation) to fit the line of
@@ -158,7 +161,7 @@ private void setLineVerticalIndentation(StyledText styledText, GC gc) {
height = ghostTextExtent.y - ghostTextExtent.y / numberOfLines;
}
- int lineIndex = styledText.getLineAtOffset(this.triggerPosition.getOffset()) + 1;
+ int lineIndex = styledText.getLineAtOffset(widgetOffset) + 1;
lineIndex = Math.min(lineIndex, styledText.getLineCount() - 1);
styledText.setLineVerticalIndent(lineIndex, height);
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorLifecycleListener.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorLifecycleListener.java
index 97857142..17f477e7 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorLifecycleListener.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorLifecycleListener.java
@@ -1,5 +1,6 @@
package com.microsoft.copilot.eclipse.ui.completion;
+import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.texteditor.ITextEditor;
@@ -48,22 +49,26 @@ public void partOpened(IWorkbenchPart part) {
* Creates the {@link CompletionHandler} for the ITextEditor of the IWorkbenchPart.
*/
public void createCompletionHandlerFor(IWorkbenchPart part) {
- ITextEditor editor = part.getAdapter(ITextEditor.class);
- if (editor == null) {
- return;
+ IEditorPart editorPart = part.getAdapter(IEditorPart.class);
+ if (editorPart != null) {
+ ITextEditor editor = editorPart.getAdapter(ITextEditor.class);
+ if (editor != null) {
+ manager.getOrCreateCompletionHandlerFor(editor);
+ manager.setActiveEditor(editor);
+ }
}
- manager.getOrCreateCompletionHandlerFor(editor);
- manager.setActiveEditor(editor);
}
void disposeCompletionHandlerFor(IWorkbenchPart part) {
- ITextEditor editor = part.getAdapter(ITextEditor.class);
- if (editor == null) {
- return;
- }
- manager.disposeCompletionHandlerFor(editor);
- if (editor.equals(manager.getActiveEditor())) {
- manager.setActiveEditor(null);
+ IEditorPart editorPart = part.getAdapter(IEditorPart.class);
+ if (editorPart != null) {
+ ITextEditor editor = editorPart.getAdapter(ITextEditor.class);
+ if (editor != null) {
+ manager.disposeCompletionHandlerFor(editor);
+ if (editor.equals(manager.getActiveEditor())) {
+ manager.setActiveEditor(null);
+ }
+ }
}
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java
index 8bea0b49..6d8d7314 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java
@@ -72,12 +72,12 @@ public static IEditorPart getActiveEditorPart() {
});
return ref.get();
}
-
+
/**
* This method retrieves the active workbench window from the event and then gets the shell associated with that
* window. It is more specific to the Eclipse framework and is typically used in handlers for commands or actions
* within the Eclipse environment.
-
+ *
* @throws ExecutionException if the active workbench window cannot be retrieved from the event.
*/
public static Shell getShellFromEvent(ExecutionEvent event) throws ExecutionException {
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
index ad306c53..4beed572 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
@@ -1,13 +1,12 @@
package com.microsoft.copilot.eclipse.ui.utils;
import java.net.URI;
-import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.core.resources.IFile;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.ITextViewer;
-import org.eclipse.swt.custom.StyledText;
+import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
@@ -79,12 +78,17 @@ public static ImageDescriptor resizeIcon(String path, int width, int height) {
* Gets the caret offset of the given text viewer.
*/
public static int getCaretOffset(ITextViewer textViewer) {
- final AtomicInteger ref = new AtomicInteger(0);
- StyledText styledText = textViewer.getTextWidget();
- SwtUtils.invokeOnDisplayThread(() -> {
- int offset = styledText.getCaretOffset();
- ref.set(offset);
- }, styledText);
- return ref.get();
+ if (textViewer == null) {
+ return 0;
+ }
+ return textViewer.getSelectedRange().x;
+ }
+
+ /**
+ * Returns the widget offset that corresponds to the given offset in the viewer's input document or -1 if
+ * there is no such offset.
+ */
+ public static int modelOffset2WidgetOffset(ITextViewer textViewer, int offset) {
+ return textViewer instanceof ITextViewerExtension5 extension ? extension.modelOffset2WidgetOffset(offset) : offset;
}
}
diff --git a/target-platform.target b/target-platform.target
index 220747c4..cf1e2950 100644
--- a/target-platform.target
+++ b/target-platform.target
@@ -2,8 +2,10 @@
-
+
+
+
+
@@ -12,12 +14,8 @@
-
-
+
org.apache.commons
@@ -27,34 +25,28 @@
-
+
-
- io.reactivex.rxjava3
- rxjava
- 3.1.10
-
org.mockito
mockito-core
5.14.2
+ jar
org.mockito
mockito-junit-jupiter
5.14.2
+ jar
-
org.objenesis
objenesis
3.4
+ jar
-
+
\ No newline at end of file
From edb5d9d6cbc6863690ca9f0e900a75a26b4adbfa Mon Sep 17 00:00:00 2001
From: ethanyhou <149548697+ethanyhou@users.noreply.github.com>
Date: Thu, 19 Dec 2024 16:57:10 +0800
Subject: [PATCH 025/690] ux - Updated icons to the plugin. (#34)
---
.../build.properties | 3 ++-
.../icons/copilot.png | Bin 707 -> 0 bytes
.../icons/gitHub_copilot_error_blue.png | Bin 0 -> 744 bytes
.../icons/gitHub_copilot_error_blue@2x.png | Bin 0 -> 1533 bytes
.../icons/github_copilot_not_signed_in_blue.png | Bin 0 -> 616 bytes
.../github_copilot_not_signed_in_blue@2x.png | Bin 0 -> 1002 bytes
.../icons/github_copilot_signed_in_blue.png | Bin 0 -> 532 bytes
.../icons/github_copilot_signed_in_blue@2x.png | Bin 0 -> 919 bytes
.../icons/signin.png | Bin 0 -> 499 bytes
.../icons/signin@2x.png | Bin 0 -> 1003 bytes
.../icons/signout.png | Bin 0 -> 496 bytes
.../icons/signout@2x.png | Bin 0 -> 1038 bytes
com.microsoft.copilot.eclipse.ui/plugin.xml | 2 +-
.../ui/handlers/ShowStatusBarMenuHandler.java | 9 +++++----
.../eclipse/ui/handlers/SignInHandler.java | 3 ---
.../eclipse/ui/handlers/SignOutHandler.java | 4 +---
.../copilot/eclipse/ui/utils/UiUtils.java | 7 +++++++
17 files changed, 16 insertions(+), 12 deletions(-)
delete mode 100644 com.microsoft.copilot.eclipse.ui/icons/copilot.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/gitHub_copilot_error_blue.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/gitHub_copilot_error_blue@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/github_copilot_not_signed_in_blue.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/github_copilot_not_signed_in_blue@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/github_copilot_signed_in_blue.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/github_copilot_signed_in_blue@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/signin.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/signin@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/signout.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/signout@2x.png
diff --git a/com.microsoft.copilot.eclipse.ui/build.properties b/com.microsoft.copilot.eclipse.ui/build.properties
index 206f1966..7d0facb6 100644
--- a/com.microsoft.copilot.eclipse.ui/build.properties
+++ b/com.microsoft.copilot.eclipse.ui/build.properties
@@ -2,4 +2,5 @@ source.. = src
output.. = target/classes
bin.includes = META-INF/,\
.,\
- plugin.xml
+ plugin.xml,\
+ icons/
diff --git a/com.microsoft.copilot.eclipse.ui/icons/copilot.png b/com.microsoft.copilot.eclipse.ui/icons/copilot.png
deleted file mode 100644
index 31996ba7e667e0ea2d414b353a40a12ce7c41e53..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 707
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
literal 0
HcmV?d00001
diff --git a/com.microsoft.copilot.eclipse.ui/icons/github_copilot_not_signed_in_blue.png b/com.microsoft.copilot.eclipse.ui/icons/github_copilot_not_signed_in_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..eb4f22c2224f7ef472a6b5224fd7d126a319a17a
GIT binary patch
literal 616
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
literal 0
HcmV?d00001
diff --git a/com.microsoft.copilot.eclipse.ui/icons/signin@2x.png b/com.microsoft.copilot.eclipse.ui/icons/signin@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..d72e0edaf9105f2ed1c2e701bdd576abd21a9021
GIT binary patch
literal 1003
zcmV`^
z6hRQJngsEK8WjXjt_UKEvZ9EdG~h`D;}5^6i-?FG6cJIBj34lz;>Hi2bWH;4MO-{+
zKrlJv;vtE-h(t_QQ1SsGix6oJ;S2vNVgbKWCf<2jO9vnu%gM56v~
zbK$`^w04v+#RzdVvs_A-0-gJR?a
z0^{TWNfdC)nq?$0uApOsp3%7QIpt-
zU6Iix`4Xu7?bje&a(;U?AHw7jF<8mO7`T-M{U=d+BRibM$lMutV)I(Mk!*&q>4Avj
zP~&2ISLJ*ANvhC1VAT5MM_kjOUo)08a5TqHT>(NQvzgq-x4e@J99BUzAV+LSUp
zzG*csoHv_!o%uL4+_v=Gj_6Mg5cn?vlw;PaVsmi)@P^!J5p!3?e{MaR!P#UVjWkX)_Mj-E%`uLZm^8Nq4ZjYj)bcf-K>zT
zc`Ze2;diq@ZjOYP<(T*TU={_WrZYK#kc%4dm7?d
zFCAEGoMXxkJMg%%1G{?C!jrZLD?eOIz70B%6Ezq}#gqMv%9CFH<#F@QIalQ7Ux&b*
zquDdOnc)m(M~0{`{f+KCZ>P@ge&PS*Sh%9lxW<5VPsm?GeG59TC*wTCVGI;GwMhp!
z!>Rk+H-OKYQ!qH>a44)4I96rqS~B%3IXO!p1rU-X3Zm%EyIyQ+@720FbM4wVM|Ctg
zldgg~5Xu2J@2}f>jAEd=FY%WL|11~$1PNaWO}!ukR=8MOlV+-W6BX>`8;3Ah(Ws>Nx-Fwh#&~!TCi9Imr66ch%QnED<~Bm5>SeYkX9!LAs|9QrEwN)
z-3mISgHSgMA}YCi=9+eB=pXf4?%n(O`0l$qLjDP8*PvY-VJ+r?0Due;fIwW1EN_!;
zplOQt>^#ZZHjeE?Rwffsf1}z-Dp^q`v?5^CQoetGvVr)b-(923Iekk4i&6}ZOMc2&
zZX~u4s3S+AqCk{l^HBi=4GG!_+ndVDBLGSnnGN{Kps%k#eV@-s8{ueS_bTlWI0{6D
zWG*33^Xq`B%iL{1n}^eVe3glBzysl_VLZNhN=fUXVqs4=YDW+mWe;nocOkyD2RSeB
zNkm7<*3twNOP}P@cmbp+7Y=m8@xXZk;_JSy+N;{ggQ3+R%RZZEF`?C2Jl?1A1`{
zQ$Z9y=cYcOVyh_BcMuU3u?gK2ijp)|3T|}eM(x9uC|GeTG=h68A}-U~AE4b^lj^1x
zlu%zqsZB+E6)}ahX_`B8JTv!^n`&G2*1FISlFXbtcg{Cw&iQ5n@UH{GClb9k$e9Ka
zh^Tx{ZBqkC^(f1R?zWTXFFMZ+@CoHl66pCQA?mi>=V!z=?ju{b_i3n
zNAKQCfe)y>08gS;EbP1~6abMV0oAG>01SdpNP@Bu%$oxPMphTVIKm;@EQul^o%1Q2On0t1H+b+WTKf3T*uj`p88
z#p4as5s(u6gNs0xs=q7;QjrE;wQN=G9S0*&6RtyZauQ^|04f4X#Zu9RZoP+U4$?`@
zVuzqLP@A}K&(D@Lz=~)zmC8CkcnxLRqX{GF_R
z018=>R0N&Ngw|wsJWCTh&xmR{aXQ!%cL{hIDgw#+`XChYNp1&MR#w8UNF%IXvxdZE
z)^fvN+iOFxy*BKu%UK=Iqa>E}d}K{bE)h0lh92RUvC$SU0bK+%Cjua(Wt=rh=5lBX
z;ZXDhn6yJR&|U;N`vBk`LLO3lM&$E=CS|=LSD^t<9acmK2Q$e~D9o=W3%RM#=!1u_
zW$zxStg3R?`~Kw!eEsxM)3+B}B1c42ex{TH!oL!YzH$QAt7Q?0rqhaLxc@+?Yc@A^
zVQlCzY}~q;Hg2i`wH7&3K(I?tdL38|r|Z&^w$kxeCie76+)F@~rOliwhz+MZ`u81B
zo8LJx{tjt6PwO|VCpj~NoS}k!~rCFgwn0
zS+EofGKxjtQmjrxf(4}5izAumX~V!MN!iCjlr^Os2fqYJ)oH1}=zSoX8OiX^aabbQ
z;a@lh{v_5t2BIiMq8$>HB2;PI`otXmMjmI&Ja8Qk;1cLXkX-Ims|L^!ubOUI5H
zVDJUY9z)QxWCZkIAJ>r+UdOHb2<;m}mc{J))k`gL_zxT30Flq`@^iTO0ssI207*qo
IM6N<$f)uaELjV8(
literal 0
HcmV?d00001
diff --git a/com.microsoft.copilot.eclipse.ui/plugin.xml b/com.microsoft.copilot.eclipse.ui/plugin.xml
index 50a70a5a..9ed13ce7 100644
--- a/com.microsoft.copilot.eclipse.ui/plugin.xml
+++ b/com.microsoft.copilot.eclipse.ui/plugin.xml
@@ -17,7 +17,7 @@
id="com.microsoft.copilot.eclipse.ui.statusBar">
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
index acf67332..72bfebfb 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
@@ -25,14 +25,13 @@
public class ShowStatusBarMenuHandler extends AbstractHandler {
private IHandlerService handlerService;
- private ImageDescriptor icon;
private AuthStatusManager authStatusManager;
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
handlerService = HandlerUtil.getActiveWorkbenchWindow(event).getService(IHandlerService.class);
authStatusManager = CopilotCore.getPlugin().getAuthStatusManager();
- icon = ImageDescriptor.createFromURL(UiUtils.class.getResource("/icons/copilot.png"));
+
MenuManager menuManager = new MenuManager();
addStatusAction(menuManager);
@@ -75,10 +74,12 @@ private String getSignInStatusBasedOnAuthResult(AuthStatusResult authStatusResul
private void addSignInOrSignOutAction(MenuManager menuManager) {
if (authStatusManager.getAuthStatusResult().isSignedIn()) {
- MenuActionFactory.createMenuAction(menuManager, Messages.menu_signOutFromGitHub, icon, handlerService,
+ ImageDescriptor signInIcon = UiUtils.buildImageDescriptorFromPngPath("/icons/signin.png");
+ MenuActionFactory.createMenuAction(menuManager, Messages.menu_signOutFromGitHub, signInIcon, handlerService,
"com.microsoft.copilot.eclipse.commands.signOut", true);
} else {
- MenuActionFactory.createMenuAction(menuManager, Messages.menu_signToGitHub, icon, handlerService,
+ ImageDescriptor signOutIcon = UiUtils.buildImageDescriptorFromPngPath("/icons/signout.png");
+ MenuActionFactory.createMenuAction(menuManager, Messages.menu_signToGitHub, signOutIcon, handlerService,
"com.microsoft.copilot.eclipse.commands.signIn", true);
}
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
index 0c85a367..800a3334 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
@@ -12,7 +12,6 @@
import com.microsoft.copilot.eclipse.core.AuthStatusManager;
import com.microsoft.copilot.eclipse.core.CopilotCore;
-import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.SignInInitiateResult;
import com.microsoft.copilot.eclipse.ui.dialogs.SignInConfirmDialog;
import com.microsoft.copilot.eclipse.ui.dialogs.SignInDialog;
@@ -27,14 +26,12 @@ public class SignInHandler extends AbstractHandler {
private static final long SIGNIN_TIMEOUT_MILLIS = 180000L;
- private CopilotLanguageServerConnection languageServer;
private AuthStatusManager authStatusManager;
/**
* Initialize the Copilot Language Server for the SignInHandler.
*/
public SignInHandler() {
- this.languageServer = CopilotCore.getPlugin().getCopilotLanguageServer();
this.authStatusManager = CopilotCore.getPlugin().getAuthStatusManager();
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
index 0dd913d7..a0699ca7 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
@@ -19,14 +19,12 @@
*/
public class SignOutHandler extends AbstractHandler {
- private CopilotLanguageServerConnection languageServer;
private AuthStatusManager authStatusManager;
/**
* Initialize the Copilot Language Server and Auth Status Manager for the SignOutHandler.
*/
public SignOutHandler() {
- this.languageServer = CopilotCore.getPlugin().getCopilotLanguageServer();
this.authStatusManager = CopilotCore.getPlugin().getAuthStatusManager();
}
@@ -34,7 +32,7 @@ public SignOutHandler() {
public Object execute(ExecutionEvent event) throws ExecutionException {
Shell shell = SwtUtils.getShellFromEvent(event);
try {
- AuthStatusResult result = this.languageServer.signOut().get();
+ AuthStatusResult result = authStatusManager.signOut();
if (!result.isSignedIn()) {
showSignOutMessage(shell);
authStatusManager.checkStatus();
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
index 4beed572..4df81a79 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
@@ -91,4 +91,11 @@ public static int getCaretOffset(ITextViewer textViewer) {
public static int modelOffset2WidgetOffset(ITextViewer textViewer, int offset) {
return textViewer instanceof ITextViewerExtension5 extension ? extension.modelOffset2WidgetOffset(offset) : offset;
}
+
+ /**
+ * Builds an image descriptor from a PNG file at the given path.
+ */
+ public static final ImageDescriptor buildImageDescriptorFromPngPath(String path) {
+ return ImageDescriptor.createFromURL(UiUtils.class.getResource(path));
+ }
}
From d31a5a7f97a65c2f9e3e5931c8bee663c3d3bb15 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Thu, 19 Dec 2024 18:49:43 +0800
Subject: [PATCH 026/690] build - Add feature and repository module to build
installation bundle (#36)
---
.azure-pipelines/nightly.yml | 84 +++++++++++++++++++
.../build.properties | 1 +
.../feature.xml | 19 +++++
com.microsoft.copilot.eclipse.feature/pom.xml | 13 +++
.../category.xml | 8 ++
.../pom.xml | 37 ++++++++
.../build.properties | 3 +-
pom.xml | 4 +
8 files changed, 168 insertions(+), 1 deletion(-)
create mode 100644 .azure-pipelines/nightly.yml
create mode 100644 com.microsoft.copilot.eclipse.feature/build.properties
create mode 100644 com.microsoft.copilot.eclipse.feature/feature.xml
create mode 100644 com.microsoft.copilot.eclipse.feature/pom.xml
create mode 100644 com.microsoft.copilot.eclipse.repository/category.xml
create mode 100644 com.microsoft.copilot.eclipse.repository/pom.xml
diff --git a/.azure-pipelines/nightly.yml b/.azure-pipelines/nightly.yml
new file mode 100644
index 00000000..10e2f133
--- /dev/null
+++ b/.azure-pipelines/nightly.yml
@@ -0,0 +1,84 @@
+name: $(Date:yyyyMMdd).$(Rev:r)
+variables:
+ - name: Codeql.Enabled
+ value: true
+resources:
+ repositories:
+ - repository: self
+ type: git
+ ref: refs/heads/main
+ - repository: MicroBuildTemplate
+ type: git
+ name: 1ESPipelineTemplates/MicroBuildTemplate
+trigger: none
+extends:
+ template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate
+ parameters:
+ pool:
+ os: linux
+ name: 1ES_JavaTooling_Pool
+ image: 1ES_JavaTooling_Ubuntu-2004
+ sdl:
+ sourceAnalysisPool:
+ name: 1ES_JavaTooling_Pool
+ image: 1ES_JavaTooling_Windows_2022
+ os: windows
+ stages:
+ - stage: Build
+ jobs:
+ - job: Build
+ displayName: GitHub-Copilot-Eclipse-Nightly
+ templateContext:
+ outputs:
+ - output: pipelineArtifact
+ artifactName: plugin
+ targetPath: '$(Build.ArtifactStagingDirectory)/plugin'
+ displayName: "Publish Artifact: plugin"
+ steps:
+ - checkout: self
+ fetchTags: false
+
+ - task: JavaToolInstaller@0
+ displayName: Use Java 17
+ inputs:
+ versionSpec: "17"
+ jdkArchitectureOption: x64
+ jdkSourceOption: PreInstalled
+
+ - task: UseNode@1
+ displayName: Use Node 20.x
+ inputs:
+ version: '20.x'
+
+ - bash: npm i
+ workingDirectory: com.microsoft.copilot.eclipse.core/copilot-agent
+ displayName: Install Copilot LS
+
+ - bash: ./mvnw clean package
+ displayName: 'Run Maven Clean and Package'
+
+ # TODO: support code sign
+ - bash: |
+ mkdir -p ./artifacts/eclipse/
+ cp ./com.microsoft.copilot.eclipse.repository/target/com.microsoft.copilot.eclipse.repository*.zip ./artifacts/eclipse/GithubCopilotForEclipse.zip
+
+ # unzip ./artifacts/eclipse/GithubCopilotForEclipse.zip "**/*.jar" "*.jar" -d ./artifacts/eclipse/folder
+ # rm ./artifacts/eclipse/GithubCopilotForEclipse.zip
+
+ # ## Workaround: Remove MD5/SHA256 Validation in artifacts.xml
+ # cd ./artifacts/eclipse/folder
+ # unzip artifacts.jar -d ./artifacts
+ # rm artifacts.jar
+ # cd artifacts
+ # sed -i -E '/download\.md5|checksum/d' ./artifacts.xml
+ # zip -R ./artifacts.jar * **/*
+ # mv ./artifacts.jar ../artifacts.jar
+ # cd ..
+ # rm -rf ./artifacts
+
+ - task: CopyFiles@2
+ displayName: Copy plugin zip
+ inputs:
+ Contents: |
+ artifacts/eclipse/GithubCopilotForEclipse.zip
+ TargetFolder: '$(Build.ArtifactStagingDirectory)/plugin'
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.feature/build.properties b/com.microsoft.copilot.eclipse.feature/build.properties
new file mode 100644
index 00000000..64f93a9f
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.feature/build.properties
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/com.microsoft.copilot.eclipse.feature/feature.xml b/com.microsoft.copilot.eclipse.feature/feature.xml
new file mode 100644
index 00000000..589fcb30
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.feature/feature.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.feature/pom.xml b/com.microsoft.copilot.eclipse.feature/pom.xml
new file mode 100644
index 00000000..4ef0a422
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.feature/pom.xml
@@ -0,0 +1,13 @@
+
+ 4.0.0
+
+ com.microsoft.copilot.eclipse
+ github-copilot-for-eclipse
+ 0.1.0-SNAPSHOT
+
+ com.microsoft.copilot.eclipse.feature
+ eclipse-feature
+ ${base.name} :: Feature
+
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.repository/category.xml b/com.microsoft.copilot.eclipse.repository/category.xml
new file mode 100644
index 00000000..1a640463
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.repository/category.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.repository/pom.xml b/com.microsoft.copilot.eclipse.repository/pom.xml
new file mode 100644
index 00000000..477bf734
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.repository/pom.xml
@@ -0,0 +1,37 @@
+
+ 4.0.0
+
+ com.microsoft.copilot.eclipse
+ github-copilot-for-eclipse
+ 0.1.0-SNAPSHOT
+
+ com.microsoft.copilot.eclipse.repository
+ eclipse-repository
+ ${base.name} :: Repository
+
+
+
+
+ org.eclipse.tycho
+ tycho-p2-director-plugin
+ ${tycho-version}
+
+
+ materialize-products
+
+ materialize-products
+
+
+
+ archive-products
+
+ archive-products
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/build.properties b/com.microsoft.copilot.eclipse.ui/build.properties
index 7d0facb6..0086cac1 100644
--- a/com.microsoft.copilot.eclipse.ui/build.properties
+++ b/com.microsoft.copilot.eclipse.ui/build.properties
@@ -3,4 +3,5 @@ output.. = target/classes
bin.includes = META-INF/,\
.,\
plugin.xml,\
- icons/
+ icons/,\
+ plugin.properties
diff --git a/pom.xml b/pom.xml
index 1ff5f28c..aa586a87 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,6 +20,10 @@
com.microsoft.copilot.eclipse.core
com.microsoft.copilot.eclipse.ui
+
+ com.microsoft.copilot.eclipse.feature
+ com.microsoft.copilot.eclipse.repository
+
com.microsoft.copilot.eclipse.core.test
com.microsoft.copilot.eclipse.ui.test
From 4d7c1f0565853a58c460cff917b8cdeb1992e0e2 Mon Sep 17 00:00:00 2001
From: ethanyhou <149548697+ethanyhou@users.noreply.github.com>
Date: Thu, 19 Dec 2024 19:06:01 +0800
Subject: [PATCH 027/690] refactor - Renamed authStatusManager to
copilotStatusManager for broader usage. (#37)
---
...ts.java => CopilotStatusManagerTests.java} | 38 +++++++++---------
.../copilot/eclipse/core/CopilotCore.java | 10 ++---
...Manager.java => CopilotStatusManager.java} | 40 +++++++++----------
.../core/lsp/CopilotLanguageServer.java | 8 ++--
.../lsp/CopilotLanguageServerConnection.java | 22 +++++-----
...usResult.java => CopilotStatusResult.java} | 4 +-
.../copilot/eclipse/ui/CopilotUi.java | 14 +++++++
.../completion/CompletionStatusListener.java | 16 ++++++++
.../ui/dialogs/SignInConfirmDialog.java | 8 ++--
.../ui/handlers/ShowStatusBarMenuHandler.java | 30 +++++++-------
.../eclipse/ui/handlers/SignInHandler.java | 8 ++--
.../eclipse/ui/handlers/SignOutHandler.java | 13 +++---
12 files changed, 120 insertions(+), 91 deletions(-)
rename com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/{AuthStatusManagerTests.java => CopilotStatusManagerTests.java} (51%)
rename com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/{AuthStatusManager.java => CopilotStatusManager.java} (59%)
rename com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/{AuthStatusResult.java => CopilotStatusResult.java} (95%)
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusListener.java
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java
similarity index 51%
rename from com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
rename to com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java
index 96d5f2d0..e5c33e01 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java
@@ -13,65 +13,65 @@
import org.mockito.junit.jupiter.MockitoExtension;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
-import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
@ExtendWith(MockitoExtension.class)
-class AuthStatusManagerTests {
+class CopilotStatusManagerTests {
@Mock
CopilotLanguageServerConnection mockConnection;
- AuthStatusManager authStatusManager;
+ CopilotStatusManager copilotStatusManager;
@BeforeEach
public void setUp() {
- authStatusManager = new AuthStatusManager(mockConnection);
+ copilotStatusManager = new CopilotStatusManager(mockConnection);
}
@Test
- void testAuthStatusResultOnSuccess() {
- AuthStatusResult expectedResult = new AuthStatusResult();
- expectedResult.setStatus(AuthStatusResult.OK);
+ void testCopilotStatusResultOnSuccess() {
+ CopilotStatusResult expectedResult = new CopilotStatusResult();
+ expectedResult.setStatus(CopilotStatusResult.OK);
when(mockConnection.checkStatus(false)).thenReturn(CompletableFuture.completedFuture(expectedResult));
- authStatusManager.checkStatus();
+ copilotStatusManager.checkStatus();
- assertEquals(AuthStatusResult.OK, authStatusManager.getAuthStatusResult().getStatus());
+ assertEquals(CopilotStatusResult.OK, copilotStatusManager.getCopilotStatusResult().getStatus());
}
@Test
void testCheckStatusLoadingWithDelay() throws InterruptedException {
String mockedUser = "mockedUser";
// Arrange
- AuthStatusResult expectedResult = new AuthStatusResult();
- expectedResult.setStatus(AuthStatusResult.OK);
+ CopilotStatusResult expectedResult = new CopilotStatusResult();
+ expectedResult.setStatus(CopilotStatusResult.OK);
expectedResult.setUser(mockedUser);
- CompletableFuture future = new CompletableFuture<>();
+ CompletableFuture future = new CompletableFuture<>();
when(mockConnection.checkStatus(false)).thenReturn(future);
// Act
- authStatusManager.checkStatus();
+ copilotStatusManager.checkStatus();
// Assert initial status is LOADING
- assertEquals(AuthStatusResult.LOADING, authStatusManager.getAuthStatusResult().getStatus());
+ assertEquals(CopilotStatusResult.LOADING, copilotStatusManager.getCopilotStatusResult().getStatus());
future.complete(expectedResult);
// Assert final status is OK
- assertEquals(AuthStatusResult.OK, authStatusManager.getAuthStatusResult().getStatus());
- assertEquals(mockedUser, authStatusManager.getAuthStatusResult().getUser());
+ assertEquals(CopilotStatusResult.OK, copilotStatusManager.getCopilotStatusResult().getStatus());
+ assertEquals(mockedUser, copilotStatusManager.getCopilotStatusResult().getUser());
}
@Test
void testCheckStatusError() {
- CompletableFuture future = new CompletableFuture<>();
+ CompletableFuture future = new CompletableFuture<>();
future.completeExceptionally(new CompletionException(new Exception("Some other error")));
when(mockConnection.checkStatus(false)).thenReturn(future);
- authStatusManager.checkStatus();
+ copilotStatusManager.checkStatus();
- assertEquals(AuthStatusResult.ERROR, authStatusManager.getAuthStatusResult().getStatus());
+ assertEquals(CopilotStatusResult.ERROR, copilotStatusManager.getCopilotStatusResult().getStatus());
}
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
index 6981e2bb..0b3501eb 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
@@ -20,7 +20,7 @@
public class CopilotCore extends Plugin {
private CopilotLanguageServerConnection copilotLanguageServer;
- private AuthStatusManager authStatusManager;
+ private CopilotStatusManager copilotStatusManager;
private CompletionProvider completionProvider;
private static CopilotCore COPILOT_CORE_PLUGIN = null;
@@ -64,9 +64,9 @@ void init() {
LanguageServerWrapper wrapper = LanguageServiceAccessor.startLanguageServer(serverDef);
this.copilotLanguageServer = new CopilotLanguageServerConnection(wrapper);
this.completionProvider = new CompletionProvider(this.copilotLanguageServer);
- this.authStatusManager = new AuthStatusManager(this.copilotLanguageServer);
+ this.copilotStatusManager = new CopilotStatusManager(this.copilotLanguageServer);
- this.authStatusManager.checkStatus();
+ this.copilotStatusManager.checkStatus();
};
Job initJob = new Job("GitHub Copilot Initialization...") {
@@ -83,8 +83,8 @@ public CopilotLanguageServerConnection getCopilotLanguageServer() {
return copilotLanguageServer;
}
- public AuthStatusManager getAuthStatusManager() {
- return authStatusManager;
+ public CopilotStatusManager getCopilotStatusManager() {
+ return copilotStatusManager;
}
public CompletionProvider getCompletionProvider() {
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/AuthStatusManager.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
similarity index 59%
rename from com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/AuthStatusManager.java
rename to com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
index 2a0cc3ff..6693a750 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/AuthStatusManager.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
@@ -5,29 +5,29 @@
import java.util.concurrent.TimeUnit;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
-import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
import com.microsoft.copilot.eclipse.core.lsp.protocol.SignInInitiateResult;
/**
* Manager for the authentication status.
*/
-public class AuthStatusManager {
+public class CopilotStatusManager {
private CopilotLanguageServerConnection connection;
- private AuthStatusResult authStatusResult;
+ private CopilotStatusResult copilotStatusResult;
private static final int CHECK_STATUS_TIMEOUT_MILLIS = 3000;
/**
- * Constructor for the AuthStatusManager.
+ * Constructor for the CopilotStatusManager.
*
* @param connection the connection to the language server.
*/
- public AuthStatusManager(CopilotLanguageServerConnection connection) {
+ public CopilotStatusManager(CopilotLanguageServerConnection connection) {
this.connection = connection;
- this.authStatusResult = new AuthStatusResult();
- this.authStatusResult.setStatus(AuthStatusResult.LOADING);
+ this.copilotStatusResult = new CopilotStatusResult();
+ this.copilotStatusResult.setStatus(CopilotStatusResult.LOADING);
}
/**
@@ -39,7 +39,7 @@ public AuthStatusManager(CopilotLanguageServerConnection connection) {
public SignInInitiateResult signInInitiate() throws InterruptedException, ExecutionException {
SignInInitiateResult result = connection.signInInitiate().get();
if (result.isAlreadySignedIn()) {
- this.authStatusResult.setStatus(AuthStatusResult.OK);
+ this.copilotStatusResult.setStatus(CopilotStatusResult.OK);
}
return result;
}
@@ -50,11 +50,11 @@ public SignInInitiateResult signInInitiate() throws InterruptedException, Execut
* @throws ExecutionException if the sign in process fails due to an execution error
* @throws InterruptedException if the sign in process is interrupted
*/
- public AuthStatusResult signInConfirm(String userCode) throws InterruptedException, ExecutionException {
- AuthStatusResult result = connection.signInConfirm(userCode).get();
+ public CopilotStatusResult signInConfirm(String userCode) throws InterruptedException, ExecutionException {
+ CopilotStatusResult result = connection.signInConfirm(userCode).get();
if (result.isSignedIn()) {
- this.authStatusResult.setStatus(AuthStatusResult.OK);
- this.authStatusResult.setUser(result.getUser());
+ this.copilotStatusResult.setStatus(CopilotStatusResult.OK);
+ this.copilotStatusResult.setUser(result.getUser());
}
return result;
}
@@ -65,10 +65,10 @@ public AuthStatusResult signInConfirm(String userCode) throws InterruptedExcepti
* @throws ExecutionException if the sign out process fails due to an execution error
* @throws InterruptedException if the sign out process is interrupted
*/
- public AuthStatusResult signOut() throws InterruptedException, ExecutionException {
- AuthStatusResult result = connection.signOut().get();
+ public CopilotStatusResult signOut() throws InterruptedException, ExecutionException {
+ CopilotStatusResult result = connection.signOut().get();
if (!result.isSignedIn()) {
- this.authStatusResult.setStatus(AuthStatusResult.NOT_SIGNED_IN);
+ this.copilotStatusResult.setStatus(CopilotStatusResult.NOT_SIGNED_IN);
}
return result;
}
@@ -77,19 +77,19 @@ public AuthStatusResult signOut() throws InterruptedException, ExecutionExceptio
* Check the login status for current machine.
*/
public void checkStatus() {
- CompletableFuture statusFuture = this.connection.checkStatus(false);
+ CompletableFuture statusFuture = this.connection.checkStatus(false);
statusFuture.orTimeout(CHECK_STATUS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS).thenAccept(result -> {
- this.authStatusResult = result;
+ this.copilotStatusResult = result;
}).exceptionally(ex -> {
// TODO: log & send telemetry
- this.authStatusResult.setStatus(AuthStatusResult.ERROR);
+ this.copilotStatusResult.setStatus(CopilotStatusResult.ERROR);
return null;
});
}
- public AuthStatusResult getAuthStatusResult() {
- return this.authStatusResult;
+ public CopilotStatusResult getCopilotStatusResult() {
+ return this.copilotStatusResult;
}
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java
index 5368266c..1818cbd5 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java
@@ -5,10 +5,10 @@
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
import org.eclipse.lsp4j.services.LanguageServer;
-import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CheckStatusParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyAcceptedParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyRejectedParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyShownParams;
@@ -25,7 +25,7 @@ public interface CopilotLanguageServer extends LanguageServer {
* Check the login status for current machine.
*/
@JsonRequest
- CompletableFuture checkStatus(CheckStatusParams param);
+ CompletableFuture checkStatus(CheckStatusParams param);
/**
* Get single completion for the given parameters.
@@ -43,13 +43,13 @@ public interface CopilotLanguageServer extends LanguageServer {
* Confirm the sign in process.
*/
@JsonRequest
- CompletableFuture signInConfirm(SignInConfirmParams param);
+ CompletableFuture signInConfirm(SignInConfirmParams param);
/**
* Sign out the current user.
*/
@JsonRequest
- CompletableFuture signOut(NullParams params);
+ CompletableFuture signOut(NullParams params);
/**
* Notify the language server that the completion was shown.
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
index 0a1e6f02..c565a8bf 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
@@ -9,11 +9,11 @@
import org.eclipse.lsp4e.LanguageServerWrapper;
import org.eclipse.lsp4j.services.LanguageServer;
-import com.microsoft.copilot.eclipse.core.AuthStatusManager;
-import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
+import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CheckStatusParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyAcceptedParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyRejectedParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyShownParams;
@@ -65,8 +65,8 @@ public int getDocumentVersion(URI uri) {
/**
* Check the login status for current machine.
*/
- public CompletableFuture checkStatus(Boolean localCheckOnly) {
- Function> fn = server -> {
+ public CompletableFuture checkStatus(Boolean localCheckOnly) {
+ Function> fn = server -> {
CheckStatusParams param = new CheckStatusParams();
param.setLocalChecksOnly(localCheckOnly);
return ((CopilotLanguageServer) server).checkStatus(param);
@@ -84,7 +84,7 @@ public CompletableFuture getCompletions(CompletionParams param
}
/**
- * Please use the {@link AuthStatusManager#signInInitiate()} method instead.
+ * Please use the {@link CopilotStatusManager#signInInitiate()} method instead.
*
* Initiate the sign in process.
*/
@@ -95,12 +95,12 @@ public CompletableFuture signInInitiate() {
}
/**
- * Please use the {@link AuthStatusManager#signInConfirm()} method instead.
+ * Please use the {@link CopilotStatusManager#signInConfirm()} method instead.
*
* Confirm the sign in process.
*/
- public CompletableFuture signInConfirm(String userCode) {
- Function> fn = (server) -> {
+ public CompletableFuture signInConfirm(String userCode) {
+ Function> fn = (server) -> {
SignInConfirmParams param = new SignInConfirmParams(userCode);
return ((CopilotLanguageServer) server).signInConfirm(param);
};
@@ -108,12 +108,12 @@ public CompletableFuture signInConfirm(String userCode) {
}
/**
- * Please use the {@link AuthStatusManager#signOut()} method instead.
+ * Please use the {@link CopilotStatusManager#signOut()} method instead.
*
* Sign out from the GitHub Copilot.
*/
- public CompletableFuture signOut() {
- Function> fn = (server) -> ((CopilotLanguageServer) server)
+ public CompletableFuture signOut() {
+ Function> fn = (server) -> ((CopilotLanguageServer) server)
.signOut(new NullParams());
return this.languageServerWrapper.execute(fn);
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/AuthStatusResult.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CopilotStatusResult.java
similarity index 95%
rename from com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/AuthStatusResult.java
rename to com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CopilotStatusResult.java
index 83a637e2..bc201a80 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/AuthStatusResult.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CopilotStatusResult.java
@@ -9,7 +9,7 @@
/**
* Result for the Authentication status.
*/
-public class AuthStatusResult {
+public class CopilotStatusResult {
public static final String OK = "OK";
public static final String ERROR = "Error";
@@ -79,7 +79,7 @@ public boolean equals(Object obj) {
if (getClass() != obj.getClass()) {
return false;
}
- AuthStatusResult other = (AuthStatusResult) obj;
+ CopilotStatusResult other = (CopilotStatusResult) obj;
return Objects.equals(status, other.status) && Objects.equals(user, other.user);
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
index 9a36a17b..cda7ecd1 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
@@ -7,7 +7,10 @@
import org.osgi.framework.BundleContext;
import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.completion.CompletionListener;
+import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.ui.completion.CompletionStatusListener;
import com.microsoft.copilot.eclipse.ui.completion.EditorLifecycleListener;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
@@ -20,6 +23,7 @@ public class CopilotUi extends Plugin {
private static final int RETRY_COUNT = 30;
private static CopilotUi COPILOT_UI_PLUGIN = null;
+ private CompletionStatusListener completionStatusListener;
private EditorLifecycleListener editorLifecycleListener;
private EditorsManager editorsManager;
@@ -55,8 +59,10 @@ public void start(BundleContext context) throws Exception {
this.editorsManager = new EditorsManager(connection, CopilotCore.getPlugin().getCompletionProvider());
this.editorLifecycleListener = new EditorLifecycleListener(editorsManager);
+ this.completionStatusListener = new CompletionStatusListener();
registerPartListener();
+ registerCompletionListener();
// Initialize the completion handler for the active editor in case we miss the event
// to initialize it.
@@ -82,6 +88,10 @@ private void registerPartListener() {
}
}
+ private void registerCompletionListener() {
+ CopilotCore.getPlugin().getCompletionProvider().addCompletionListener(this.completionStatusListener);
+ }
+
private void initCompletionHandlerForActiveEditor() {
IEditorPart editorPart = SwtUtils.getActiveEditorPart();
if (editorPart != null) {
@@ -95,5 +105,9 @@ private void unregisterPartListener() {
window.getPartService().removePartListener(this.editorLifecycleListener);
}
}
+
+ private void unregisterCompletionListener() {
+ CopilotCore.getPlugin().getCompletionProvider().removeCompletionListener(this.completionStatusListener);
+ }
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusListener.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusListener.java
new file mode 100644
index 00000000..01a6e637
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusListener.java
@@ -0,0 +1,16 @@
+package com.microsoft.copilot.eclipse.ui.completion;
+
+import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
+import com.microsoft.copilot.eclipse.core.completion.CompletionListener;
+import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
+
+/**
+ * Listener for tracking copilot completion status.
+ */
+public class CompletionStatusListener implements CompletionListener {
+
+ @Override
+ public void onCompletionResolved(CompletionCollection completions) {
+ // do nothing
+ }
+}
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
index 998ca5f5..417ef6d0 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
@@ -12,7 +12,7 @@
import org.eclipse.swt.widgets.Shell;
import com.microsoft.copilot.eclipse.core.CopilotCore;
-import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
import com.microsoft.copilot.eclipse.ui.i18n.Messages;
/**
@@ -22,7 +22,7 @@ public class SignInConfirmDialog extends ProgressMonitorDialog {
private final String userCode;
private final long timeout;
- private CompletableFuture future;
+ private CompletableFuture future;
private IStatus status;
/**
@@ -73,7 +73,7 @@ public void run(IProgressMonitor monitor) throws InvocationTargetException, Inte
try {
future = CompletableFuture.supplyAsync(() -> {
try {
- return CopilotCore.getPlugin().getAuthStatusManager().signInConfirm(userCode);
+ return CopilotCore.getPlugin().getCopilotStatusManager().signInConfirm(userCode);
} catch (Exception e) {
// TODO: log & send telemetry
return null;
@@ -118,7 +118,7 @@ private void waitForAuthorization(IProgressMonitor monitor) throws InterruptedEx
}
private void handleAuthorizationResult() throws ExecutionException, InterruptedException {
- AuthStatusResult result = future.get();
+ CopilotStatusResult result = future.get();
String errorMsg = null;
if (result == null || !result.isSignedIn()) {
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
index 72bfebfb..23461903 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
@@ -13,9 +13,9 @@
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.handlers.IHandlerService;
-import com.microsoft.copilot.eclipse.core.AuthStatusManager;
import com.microsoft.copilot.eclipse.core.CopilotCore;
-import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
+import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
import com.microsoft.copilot.eclipse.ui.i18n.Messages;
import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
@@ -25,17 +25,17 @@
public class ShowStatusBarMenuHandler extends AbstractHandler {
private IHandlerService handlerService;
- private AuthStatusManager authStatusManager;
+ private CopilotStatusManager copilotStatusManager;
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
handlerService = HandlerUtil.getActiveWorkbenchWindow(event).getService(IHandlerService.class);
- authStatusManager = CopilotCore.getPlugin().getAuthStatusManager();
+ copilotStatusManager = CopilotCore.getPlugin().getCopilotStatusManager();
MenuManager menuManager = new MenuManager();
addStatusAction(menuManager);
- if (!authStatusManager.getAuthStatusResult().isLoading()) {
+ if (!copilotStatusManager.getCopilotStatusResult().isLoading()) {
menuManager.add(new Separator());
addSignInOrSignOutAction(menuManager);
}
@@ -47,25 +47,25 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
}
private void addStatusAction(MenuManager menuManager) {
- String signInStatus = getSignInStatusBasedOnAuthResult(authStatusManager.getAuthStatusResult());
+ String signInStatus = getSignInStatusBasedOnAuthResult(copilotStatusManager.getCopilotStatusResult());
String signInStatusTitle = Messages.menu_signInStatus + ": " + signInStatus;
MenuActionFactory.createMenuAction(menuManager, signInStatusTitle, handlerService, signInStatus, false);
}
- private String getSignInStatusBasedOnAuthResult(AuthStatusResult authStatusResult) {
- switch (authStatusResult.getStatus()) {
- case AuthStatusResult.OK:
+ private String getSignInStatusBasedOnAuthResult(CopilotStatusResult copilotStatusResult) {
+ switch (copilotStatusResult.getStatus()) {
+ case CopilotStatusResult.OK:
return Messages.menu_signInStatus_ready;
- case AuthStatusResult.ERROR:
+ case CopilotStatusResult.ERROR:
return Messages.menu_signInStatus_unknownError;
- case AuthStatusResult.LOADING:
+ case CopilotStatusResult.LOADING:
return Messages.menu_signInStatus_loading;
- case AuthStatusResult.NOT_SIGNED_IN:
+ case CopilotStatusResult.NOT_SIGNED_IN:
return Messages.menu_signInStatus_notSignedInToGitHub;
- case AuthStatusResult.WARNING:
+ case CopilotStatusResult.WARNING:
return Messages.menu_signInStatus_agentWarning;
- case AuthStatusResult.NOT_AUTHORIZED:
+ case CopilotStatusResult.NOT_AUTHORIZED:
return Messages.menu_signInStatus_notAuthorized;
default:
return Messages.menu_signInStatus_loading;
@@ -73,7 +73,7 @@ private String getSignInStatusBasedOnAuthResult(AuthStatusResult authStatusResul
}
private void addSignInOrSignOutAction(MenuManager menuManager) {
- if (authStatusManager.getAuthStatusResult().isSignedIn()) {
+ if (copilotStatusManager.getCopilotStatusResult().isSignedIn()) {
ImageDescriptor signInIcon = UiUtils.buildImageDescriptorFromPngPath("/icons/signin.png");
MenuActionFactory.createMenuAction(menuManager, Messages.menu_signOutFromGitHub, signInIcon, handlerService,
"com.microsoft.copilot.eclipse.commands.signOut", true);
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
index 800a3334..5aa48346 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
@@ -10,8 +10,8 @@
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;
-import com.microsoft.copilot.eclipse.core.AuthStatusManager;
import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
import com.microsoft.copilot.eclipse.core.lsp.protocol.SignInInitiateResult;
import com.microsoft.copilot.eclipse.ui.dialogs.SignInConfirmDialog;
import com.microsoft.copilot.eclipse.ui.dialogs.SignInDialog;
@@ -26,13 +26,13 @@ public class SignInHandler extends AbstractHandler {
private static final long SIGNIN_TIMEOUT_MILLIS = 180000L;
- private AuthStatusManager authStatusManager;
+ private CopilotStatusManager copilotStatusManager;
/**
* Initialize the Copilot Language Server for the SignInHandler.
*/
public SignInHandler() {
- this.authStatusManager = CopilotCore.getPlugin().getAuthStatusManager();
+ this.copilotStatusManager = CopilotCore.getPlugin().getCopilotStatusManager();
}
@Override
@@ -55,7 +55,7 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
}
private SignInInitiateResult initiateSignIn() throws Exception {
- return this.authStatusManager.signInInitiate();
+ return this.copilotStatusManager.signInInitiate();
}
private void showAlreadySignedInMessage(Shell shell) {
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
index a0699ca7..19b8e189 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
@@ -7,10 +7,9 @@
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;
-import com.microsoft.copilot.eclipse.core.AuthStatusManager;
import com.microsoft.copilot.eclipse.core.CopilotCore;
-import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
-import com.microsoft.copilot.eclipse.core.lsp.protocol.AuthStatusResult;
+import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
import com.microsoft.copilot.eclipse.ui.i18n.Messages;
import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
@@ -19,23 +18,23 @@
*/
public class SignOutHandler extends AbstractHandler {
- private AuthStatusManager authStatusManager;
+ private CopilotStatusManager copilotStatusManager;
/**
* Initialize the Copilot Language Server and Auth Status Manager for the SignOutHandler.
*/
public SignOutHandler() {
- this.authStatusManager = CopilotCore.getPlugin().getAuthStatusManager();
+ this.copilotStatusManager = CopilotCore.getPlugin().getCopilotStatusManager();
}
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
Shell shell = SwtUtils.getShellFromEvent(event);
try {
- AuthStatusResult result = authStatusManager.signOut();
+ CopilotStatusResult result = copilotStatusManager.signOut();
if (!result.isSignedIn()) {
showSignOutMessage(shell);
- authStatusManager.checkStatus();
+ copilotStatusManager.checkStatus();
}
} catch (Exception e) {
handleSignOutException(shell, e);
From b51657512f8dce65c11774539bc733a9b2bf07ec Mon Sep 17 00:00:00 2001
From: ethanyhou <149548697+ethanyhou@users.noreply.github.com>
Date: Fri, 20 Dec 2024 10:31:18 +0800
Subject: [PATCH 028/690] fix - Update plugin.xml icon png name (#38)
---
...blue.png => github_copilot_error_blue.png} | Bin
...x.png => github_copilot_error_blue@2x.png} | Bin
com.microsoft.copilot.eclipse.ui/plugin.xml | 40 +++++++++---------
.../eclipse/ui/dialogs/SignInDialog.java | 2 +-
.../eclipse/ui/handlers/SignInHandler.java | 4 +-
.../eclipse/ui/handlers/SignOutHandler.java | 2 +-
.../copilot/eclipse/ui/i18n/Messages.java | 6 +--
.../eclipse/ui/i18n/messages.properties | 6 +--
8 files changed, 30 insertions(+), 30 deletions(-)
rename com.microsoft.copilot.eclipse.ui/icons/{gitHub_copilot_error_blue.png => github_copilot_error_blue.png} (100%)
rename com.microsoft.copilot.eclipse.ui/icons/{gitHub_copilot_error_blue@2x.png => github_copilot_error_blue@2x.png} (100%)
diff --git a/com.microsoft.copilot.eclipse.ui/icons/gitHub_copilot_error_blue.png b/com.microsoft.copilot.eclipse.ui/icons/github_copilot_error_blue.png
similarity index 100%
rename from com.microsoft.copilot.eclipse.ui/icons/gitHub_copilot_error_blue.png
rename to com.microsoft.copilot.eclipse.ui/icons/github_copilot_error_blue.png
diff --git a/com.microsoft.copilot.eclipse.ui/icons/gitHub_copilot_error_blue@2x.png b/com.microsoft.copilot.eclipse.ui/icons/github_copilot_error_blue@2x.png
similarity index 100%
rename from com.microsoft.copilot.eclipse.ui/icons/gitHub_copilot_error_blue@2x.png
rename to com.microsoft.copilot.eclipse.ui/icons/github_copilot_error_blue@2x.png
diff --git a/com.microsoft.copilot.eclipse.ui/plugin.xml b/com.microsoft.copilot.eclipse.ui/plugin.xml
index 9ed13ce7..466def52 100644
--- a/com.microsoft.copilot.eclipse.ui/plugin.xml
+++ b/com.microsoft.copilot.eclipse.ui/plugin.xml
@@ -17,7 +17,7 @@
id="com.microsoft.copilot.eclipse.ui.statusBar">
@@ -28,17 +28,17 @@
-
-
-
-
-
+ id="com.microsoft.copilot.eclipse.commands.showStatusBarMenu"
+ name="%command.copilotForEclipsePlugin.name">
+
+
+
+
+
@@ -60,14 +60,14 @@
class="com.microsoft.copilot.eclipse.ui.handlers.ShowStatusBarMenuHandler"
commandId="com.microsoft.copilot.eclipse.commands.showStatusBarMenu">
-
-
-
-
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInDialog.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInDialog.java
index 38e100b3..7d869924 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInDialog.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInDialog.java
@@ -66,7 +66,7 @@ private void createDeviceCodeSection(Composite composite) {
private void createWebsiteSection(Composite composite) {
Label websiteLabel = new Label(composite, SWT.NONE);
- websiteLabel.setText(Messages.signInDialog_info_gitHubWebSitePrefix);
+ websiteLabel.setText(Messages.signInDialog_info_githubWebSitePrefix);
Link websiteLink = new Link(composite, SWT.NONE);
websiteLink.setText("" + this.signInInitiateResult.getVerificationUri() + "");
websiteLink.addSelectionListener(new SelectionAdapter() {
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
index 5aa48346..358afaaf 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
@@ -88,7 +88,7 @@ private void handleSignInConfirmation(Shell shell, SignInConfirmDialog signInCon
}
private void showSignInSuccessMessage(Shell shell) {
- MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_gitHubCopilot,
+ MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_githubCopilot,
Messages.signInHandler_msgDialog_signInSuccess);
}
@@ -98,7 +98,7 @@ private void showSignInFailMessage(Shell shell, IStatus status) {
msg += ": " + status.getMessage();
}
msg += ". ";
- MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_gitHubCopilot,
+ MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_githubCopilot,
msg + Messages.signInHandler_msgDialog_signInFailedTryAgain);
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
index 19b8e189..3c1c0971 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
@@ -54,7 +54,7 @@ private void handleSignOutException(Shell shell, Exception e) {
}
private void showSignOutMessage(Shell shell) {
- MessageDialog.openInformation(shell, Messages.signOutHandler_msgDialog_gitHubCopilot,
+ MessageDialog.openInformation(shell, Messages.signOutHandler_msgDialog_githubCopilot,
Messages.signOutHandler_msgDialog_signOutSuccess);
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
index d17b897e..fcf7264b 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
@@ -21,21 +21,21 @@ public final class Messages extends NLS {
public static String signInDialog_button_copyOpen;
public static String signInDialog_info_instructions;
public static String signInDialog_info_deviceCodePrefix;
- public static String signInDialog_info_gitHubWebSitePrefix;
+ public static String signInDialog_info_githubWebSitePrefix;
public static String signInConfirmDialog_progress;
public static String signInConfirmDialog_progressSuffix;
public static String signInConfirmDialog_progressTimeout;
public static String signInConfirmDialog_progressCanceled;
public static String signInConfirmDialog_authResult_notSignedIn;
public static String signInConfirmDialog_authResult_notAuthed;
- public static String signInHandler_msgDialog_gitHubCopilot;
+ public static String signInHandler_msgDialog_githubCopilot;
public static String signInHandler_msgDialog_title;
public static String signInHandler_msgDialog_alreadySignedIn;
public static String signInHandler_msgDialog_signInSuccess;
public static String signInHandler_msgDialog_signInFailed;
public static String signInHandler_msgDialog_signInFailedTryAgain;
public static String signInHandler_msgDialog_signInFailedFailure;
- public static String signOutHandler_msgDialog_gitHubCopilot;
+ public static String signOutHandler_msgDialog_githubCopilot;
public static String signOutHandler_msgDialog_signOutSuccess;
public static String signOutHandler_msgDialog_signOutFailed;
public static String signOutHandler_msgDialog_signOutFailedFailure;
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
index 50672d46..5a1d1015 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
@@ -13,20 +13,20 @@ signInDialog_button_cancel=Cancel
signInDialog_button_copyOpen=Copy Code and Open
signInDialog_info_instructions=GitHub Copilot uses a GitHub account. Please enter the following code on the GitHub website to authorize your GitHub account with GitHub Copilot.
signInDialog_info_deviceCodePrefix=Device code:
-signInDialog_info_gitHubWebSitePrefix=GitHub website:
+signInDialog_info_githubWebSitePrefix=GitHub website:
signInConfirmDialog_progress=Waiting for GitHub Copilot authorization...
signInConfirmDialog_progressTimeout=Authorization request failed: Process timed out.
signInConfirmDialog_progressCanceled=Authorization request failed: process aborted.
signInConfirmDialog_authResult_notSignedIn=Authorization request failed: Not signed in.
signInConfirmDialog_authResult_notAuthed=Authorization request failed: Your subscription may be expired.
-signInHandler_msgDialog_gitHubCopilot=GitHub Copilot
+signInHandler_msgDialog_githubCopilot=GitHub Copilot
signInHandler_msgDialog_title=Sign In to GitHub
signInHandler_msgDialog_alreadySignedIn=User already signed in.
signInHandler_msgDialog_signInSuccess=You have successfully signed in and authorized GitHub Copilot access to your GitHub account.
signInHandler_msgDialog_signInFailed=Unable to sign in to GitHub Copilot at this time
signInHandler_msgDialog_signInFailedTryAgain= Please try again to resume use of GitHub Copilot features.
signInHandler_msgDialog_signInFailedFailure=Copilot Sign In Failure
-signOutHandler_msgDialog_gitHubCopilot=GitHub Copilot
+signOutHandler_msgDialog_githubCopilot=GitHub Copilot
signOutHandler_msgDialog_signOutSuccess=You have successfully signed out from Copilot.
signOutHandler_msgDialog_signOutFailed=Unable to sign out to GitHub Copilot at this time
signOutHandler_msgDialog_signOutFailedFailure=Copilot Sign Out Failure
\ No newline at end of file
From 83e0f6a78004d35f39aa0ff6a014b3e81150e2f1 Mon Sep 17 00:00:00 2001
From: yanshudan <1397370237@qq.com>
Date: Fri, 20 Dec 2024 13:31:52 +0800
Subject: [PATCH 029/690] feat - Add eclipse console logger (#30)
---
.../META-INF/MANIFEST.MF | 3 +-
.../copilot/eclipse/core/CopilotCore.java | 2 +
.../copilot/eclipse/core/enums/LogLevel.java | 18 ++++
.../core/logger/CopilotForEclipseLogger.java | 79 ++++++++++++++++
.../handlers/EclipseConsoleHandler.java | 90 +++++++++++++++++++
.../core/lsp/LsStreamConnectionProvider.java | 4 +-
launch/plugin_debug_configuration.launch | 1 +
7 files changed, 195 insertions(+), 2 deletions(-)
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/enums/LogLevel.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/CopilotForEclipseLogger.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/handlers/EclipseConsoleHandler.java
diff --git a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
index bdcc5012..b65dbb85 100644
--- a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
@@ -19,4 +19,5 @@ Require-Bundle: org.eclipse.lsp4e;bundle-version="0.18.12",
org.eclipse.lsp4j.jsonrpc;bundle-version="0.23.1",
org.apache.commons.lang3;bundle-version="3.17.0",
org.eclipse.jdt.annotation;bundle-version="2.3.0",
- org.eclipse.jface.text
+ org.eclipse.jface.text,
+ com.google.gson;bundle-version="2.11.0"
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
index 0b3501eb..5165f912 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
@@ -11,6 +11,7 @@
import org.osgi.framework.BundleContext;
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
+import com.microsoft.copilot.eclipse.core.logger.CopilotForEclipseLogger;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
/**
@@ -24,6 +25,7 @@ public class CopilotCore extends Plugin {
private CompletionProvider completionProvider;
private static CopilotCore COPILOT_CORE_PLUGIN = null;
+ public static final CopilotForEclipseLogger LOGGER = new CopilotForEclipseLogger(CopilotCore.class.getName());
/**
* Creates the Copilot core plugin. The plugin is created automatically by the Eclipse framework. Clients must not
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/enums/LogLevel.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/enums/LogLevel.java
new file mode 100644
index 00000000..f7de0772
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/enums/LogLevel.java
@@ -0,0 +1,18 @@
+package com.microsoft.copilot.eclipse.core.enums;
+
+/**
+ * The event type enum.
+ */
+public enum LogLevel {
+ INFO("INFO"), WARNING("WARNING"), ERROR("ERROR");
+
+ private String value;
+
+ LogLevel(String string) {
+ this.value = string;
+ }
+
+ public String getValue() {
+ return this.value;
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/CopilotForEclipseLogger.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/CopilotForEclipseLogger.java
new file mode 100644
index 00000000..c1751613
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/CopilotForEclipseLogger.java
@@ -0,0 +1,79 @@
+package com.microsoft.copilot.eclipse.core.logger;
+
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import org.eclipse.core.runtime.Platform;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+import com.microsoft.copilot.eclipse.core.enums.LogLevel;
+import com.microsoft.copilot.eclipse.core.logger.handlers.EclipseConsoleHandler;
+
+/**
+ * The logger for Copilot for Eclipse.
+ */
+public class CopilotForEclipseLogger {
+ //TODO: migrate to xml configuration
+ private Logger logger;
+
+ /**
+ * Constructor.
+ *
+ * @param name the name of the logger
+ *
+ */
+ public CopilotForEclipseLogger(String name) {
+ logger = Logger.getLogger(name);
+ setupLoggers(logger);
+ }
+
+ /**
+ * Log the message.
+ *
+ * @param lvl the log level
+ * @param parameters the parameters
+ */
+ public void log(LogLevel lvl, Object... parameters) {
+ Level level = map2Level(lvl);
+ LogRecord logRecord = new LogRecord(level, "");
+ logRecord.setParameters(new Object[] { lvl, parameters });
+ logger.log(logRecord);
+ }
+
+ /**
+ * Set up the loggers.
+ *
+ * @param LOGGER the logger
+ */
+ private static void setupLoggers(Logger logger) {
+ logger.setUseParentHandlers(false);
+ Bundle bundle = FrameworkUtil.getBundle(CopilotForEclipseLogger.class);
+ if (bundle == null) {
+ return;
+ }
+ EclipseConsoleHandler consoleHandler = new EclipseConsoleHandler(Platform.getLog(bundle));
+ consoleHandler.setLevel(Level.ALL);
+ logger.addHandler(consoleHandler);
+ }
+
+ /**
+ * Map the LogLevel to the Level.
+ *
+ * @param level the LogLevel
+ * @return the Level
+ */
+ private Level map2Level(LogLevel level) {
+ switch (level) {
+ case INFO:
+ return Level.INFO;
+ case WARNING:
+ return Level.WARNING;
+ case ERROR:
+ return Level.SEVERE;
+ default:
+ return Level.INFO;
+ }
+ }
+}
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/handlers/EclipseConsoleHandler.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/handlers/EclipseConsoleHandler.java
new file mode 100644
index 00000000..c0acb133
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/handlers/EclipseConsoleHandler.java
@@ -0,0 +1,90 @@
+package com.microsoft.copilot.eclipse.core.logger.handlers;
+
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+
+import com.google.gson.Gson;
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.Status;
+
+import com.microsoft.copilot.eclipse.core.Constants;
+import com.microsoft.copilot.eclipse.core.enums.LogLevel;
+
+/**
+ * The log appender to send info and error messages to the Eclipse console.
+ */
+public class EclipseConsoleHandler extends Handler {
+ private ILog logger;
+
+ /**
+ * Constructor.
+ */
+ public EclipseConsoleHandler(ILog logger) {
+ this.logger = logger;
+ }
+
+ @Override
+ public void publish(LogRecord logRecord) {
+ if (logger == null) {
+ return;
+ }
+ Object[] property = logRecord.getParameters();
+ if (property == null || property.length < 2) {
+ return;
+ }
+ if (!(property[0] instanceof LogLevel)) {
+ return;
+ }
+ LogLevel lvl = (LogLevel) property[0];
+ Object[] params = (Object[]) property[1];
+ // TODO: do we need to add objects in the log message?
+ int level = map2StatusLevel(lvl);
+ logger.log(new Status(level, Constants.PLUGIN_ID, getFormatedMessage(params), getThrowable(params)));
+ }
+
+ private String getFormatedMessage(Object[] properties) {
+ String str = "";
+ for (int i = 0; i < properties.length; i++) {
+ str += "argv" + i + " = ";
+ try {
+ str += new Gson().toJson(properties[i]);
+ } catch (Exception e) {
+ str += "exceptionInToJson";
+ }
+ str += " ;";
+ }
+ return str;
+ }
+
+ private Throwable getThrowable(Object[] properties) {
+ for (int i = 1; i < properties.length; i++) {
+ if (properties[i] instanceof Throwable) {
+ return (Throwable) properties[i];
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void flush() {
+ // do nothing
+ }
+
+ @Override
+ public void close() throws SecurityException {
+ // do nothing
+ }
+
+ private int map2StatusLevel(LogLevel level) {
+ switch (level) {
+ case INFO:
+ return Status.INFO;
+ case WARNING:
+ return Status.WARNING;
+ case ERROR:
+ return Status.ERROR;
+ default:
+ return Status.INFO;
+ }
+ }
+}
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
index 48ba83c5..89e79fee 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
@@ -43,7 +43,9 @@ public Object getInitializationOptions(@Nullable URI rootUri) {
@Override
public void start() throws IOException {
- Path binary = findBinary();
+ // load lsp binary
+ // call normalize to remove any relative path components and avoid "FILE_PATH_TOO_LONG" error
+ Path binary = findBinary().normalize();
if (binary == null) {
throw new IOException("Could not find the language server binary");
}
diff --git a/launch/plugin_debug_configuration.launch b/launch/plugin_debug_configuration.launch
index fec96a04..f937ebb3 100644
--- a/launch/plugin_debug_configuration.launch
+++ b/launch/plugin_debug_configuration.launch
@@ -151,6 +151,7 @@
+
From 52816a1a66b2bfcd1324c68748a2f57df9015306 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Fri, 20 Dec 2024 16:03:22 +0800
Subject: [PATCH 030/690] fix - Check the status before triggering completion
(#42)
---
.../core/CopilotStatusManagerTests.java | 9 ++++-----
.../completion/CompletionProviderTests.java | 20 ++++++++++++++++++-
.../.settings/org.eclipse.jdt.core.prefs | 2 +-
.../copilot/eclipse/core/CopilotCore.java | 2 +-
.../eclipse/core/CopilotStatusManager.java | 20 ++++++++++++-------
.../core/completion/CompletionProvider.java | 11 ++++++++--
.../.settings/org.eclipse.jdt.core.prefs | 2 +-
.../ui/completion/CompletionManager.java | 4 ++--
.../ui/handlers/ShowStatusBarMenuHandler.java | 14 +++++++------
9 files changed, 58 insertions(+), 26 deletions(-)
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java
index e5c33e01..2286368d 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java
@@ -35,7 +35,7 @@ void testCopilotStatusResultOnSuccess() {
copilotStatusManager.checkStatus();
- assertEquals(CopilotStatusResult.OK, copilotStatusManager.getCopilotStatusResult().getStatus());
+ assertEquals(CopilotStatusResult.OK, copilotStatusManager.getCopilotStatus());
}
@Test
@@ -53,13 +53,12 @@ void testCheckStatusLoadingWithDelay() throws InterruptedException {
copilotStatusManager.checkStatus();
// Assert initial status is LOADING
- assertEquals(CopilotStatusResult.LOADING, copilotStatusManager.getCopilotStatusResult().getStatus());
+ assertEquals(CopilotStatusResult.LOADING, copilotStatusManager.getCopilotStatus());
future.complete(expectedResult);
// Assert final status is OK
- assertEquals(CopilotStatusResult.OK, copilotStatusManager.getCopilotStatusResult().getStatus());
- assertEquals(mockedUser, copilotStatusManager.getCopilotStatusResult().getUser());
+ assertEquals(CopilotStatusResult.OK, copilotStatusManager.getCopilotStatus());
}
@Test
@@ -71,7 +70,7 @@ void testCheckStatusError() {
copilotStatusManager.checkStatus();
- assertEquals(CopilotStatusResult.ERROR, copilotStatusManager.getCopilotStatusResult().getStatus());
+ assertEquals(CopilotStatusResult.ERROR, copilotStatusManager.getCopilotStatus());
}
}
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionProviderTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionProviderTests.java
index e1dadf29..49243404 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionProviderTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionProviderTests.java
@@ -2,6 +2,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -19,9 +20,11 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
@ExtendWith(MockitoExtension.class)
class CompletionProviderTests {
@@ -29,15 +32,19 @@ class CompletionProviderTests {
@Mock
private CopilotLanguageServerConnection mockLsConnection;
+ @Mock
+ private CopilotStatusManager mockStatusManager;
+
@Mock
private CompletionListener mockListener;
@Test
void testShouldNotifyListenersOnCompletion() throws OperationCanceledException, InterruptedException {
+ when(mockStatusManager.getCopilotStatus()).thenReturn(CopilotStatusResult.OK);
when(mockLsConnection.getCompletions(any()))
.thenReturn(CompletableFuture.completedFuture(new CompletionResult(List.of(mock(CompletionItem.class)))));
- CompletionProvider completionProvider = new CompletionProvider(mockLsConnection);
+ CompletionProvider completionProvider = new CompletionProvider(mockLsConnection, mockStatusManager);
completionProvider.addCompletionListener(mockListener);
Position position = new Position(0, 0);
completionProvider.triggerCompletion("file://test.java", position, 1);
@@ -46,4 +53,15 @@ void testShouldNotifyListenersOnCompletion() throws OperationCanceledException,
verify(mockLsConnection, times(1)).getCompletions(any());
}
+ @Test
+ void testShouldNotTriggerCompletionWhenNotSignedIn() throws OperationCanceledException, InterruptedException {
+ when(mockStatusManager.getCopilotStatus()).thenReturn(CopilotStatusResult.NOT_SIGNED_IN);
+
+ CompletionProvider completionProvider = new CompletionProvider(mockLsConnection, mockStatusManager);
+ completionProvider.addCompletionListener(mockListener);
+ Position position = new Position(0, 0);
+ completionProvider.triggerCompletion("file://test.java", position, 1);
+ verify(mockLsConnection, never()).getCompletions(any());
+ }
+
}
diff --git a/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs b/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs
index aa5854b6..21eccc09 100644
--- a/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs
+++ b/com.microsoft.copilot.eclipse.core/.settings/org.eclipse.jdt.core.prefs
@@ -134,7 +134,7 @@ org.eclipse.jdt.core.formatter.indent_empty_lines=false
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
org.eclipse.jdt.core.formatter.indentation.size=2
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
index 5165f912..844e0c01 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
@@ -65,8 +65,8 @@ void init() {
LanguageServerWrapper wrapper = LanguageServiceAccessor.startLanguageServer(serverDef);
this.copilotLanguageServer = new CopilotLanguageServerConnection(wrapper);
- this.completionProvider = new CompletionProvider(this.copilotLanguageServer);
this.copilotStatusManager = new CopilotStatusManager(this.copilotLanguageServer);
+ this.completionProvider = new CompletionProvider(this.copilotLanguageServer, copilotStatusManager);
this.copilotStatusManager.checkStatus();
};
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
index 6693a750..06c5c5cf 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
@@ -18,7 +18,7 @@ public class CopilotStatusManager {
private CopilotStatusResult copilotStatusResult;
private static final int CHECK_STATUS_TIMEOUT_MILLIS = 3000;
-
+
/**
* Constructor for the CopilotStatusManager.
*
@@ -32,7 +32,7 @@ public CopilotStatusManager(CopilotLanguageServerConnection connection) {
/**
* Initiate the sign in process.
-
+ *
* @throws ExecutionException if the sign in initiate process fails due to an execution error
* @throws InterruptedException if the sign in initiate process is interrupted
*/
@@ -46,7 +46,7 @@ public SignInInitiateResult signInInitiate() throws InterruptedException, Execut
/**
* Confirm the sign in process.
-
+ *
* @throws ExecutionException if the sign in process fails due to an execution error
* @throws InterruptedException if the sign in process is interrupted
*/
@@ -61,7 +61,7 @@ public CopilotStatusResult signInConfirm(String userCode) throws InterruptedExce
/**
* Sign out from the GitHub Copilot.
-
+ *
* @throws ExecutionException if the sign out process fails due to an execution error
* @throws InterruptedException if the sign out process is interrupted
*/
@@ -84,12 +84,18 @@ public void checkStatus() {
}).exceptionally(ex -> {
// TODO: log & send telemetry
this.copilotStatusResult.setStatus(CopilotStatusResult.ERROR);
-
+
return null;
});
}
- public CopilotStatusResult getCopilotStatusResult() {
- return this.copilotStatusResult;
+ /**
+ * Get the current status of the copilot.
+ */
+ public String getCopilotStatus() {
+ if (this.copilotStatusResult == null) {
+ return CopilotStatusResult.LOADING;
+ }
+ return this.copilotStatusResult.getStatus();
}
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
index 5b362169..4ee87467 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
@@ -1,6 +1,7 @@
package com.microsoft.copilot.eclipse.core.completion;
import java.util.LinkedHashSet;
+import java.util.Objects;
import java.util.Set;
import org.eclipse.core.runtime.IStatus;
@@ -8,10 +9,12 @@
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.lsp4j.Position;
+import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionDocument;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
/**
* Provider for inline completion.
@@ -19,13 +22,14 @@
public class CompletionProvider implements IJobChangeListener {
private CompletionJob completionJob;
-
private Set completionListeners;
+ private CopilotStatusManager statusManager;
/**
* Creates a new completion provider.
*/
- public CompletionProvider(CopilotLanguageServerConnection lsConnection) {
+ public CompletionProvider(CopilotLanguageServerConnection lsConnection, CopilotStatusManager statusManager) {
+ this.statusManager = statusManager;
this.completionJob = new CompletionJob(lsConnection);
this.completionJob.addJobChangeListener(this);
this.completionListeners = new LinkedHashSet<>();
@@ -39,6 +43,9 @@ public CompletionProvider(CopilotLanguageServerConnection lsConnection) {
* @param documentVersion the version of the document.
*/
public void triggerCompletion(String uriString, Position position, int documentVersion) {
+ if (!Objects.equals(statusManager.getCopilotStatus(), CopilotStatusResult.OK)) {
+ return;
+ }
this.completionJob.cancel();
this.completionJob.setCompletionParams(null);
CompletionDocument completionDoc = new CompletionDocument(uriString, position);
diff --git a/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs b/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
index aa5854b6..21eccc09 100644
--- a/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
+++ b/com.microsoft.copilot.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
@@ -134,7 +134,7 @@ org.eclipse.jdt.core.formatter.indent_empty_lines=false
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
org.eclipse.jdt.core.formatter.indentation.size=2
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
index e2e6d787..6c812660 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
@@ -142,10 +142,10 @@ public void paintControl(PaintEvent e) {
String remainingLines = this.completions.getRemainingLines();
if (StringUtils.isNotBlank(remainingLines)) {
int lineHeight = styledText.getLineHeight();
- int fontHeightt = gc.getFontMetrics().getHeight();
+ int fontHeight = gc.getFontMetrics().getHeight();
int x = styledText.getLeftMargin();
Point offsetLocation = styledText.getLocationAtOffset(widgetOffset);
- int y = offsetLocation.y + lineHeight * 2 - fontHeightt;
+ int y = offsetLocation.y + lineHeight * 2 - fontHeight;
gc.drawText(remainingLines, x, y, true);
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
index 23461903..7382e60a 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
@@ -1,5 +1,7 @@
package com.microsoft.copilot.eclipse.ui.handlers;
+import java.util.Objects;
+
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
@@ -34,8 +36,8 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
MenuManager menuManager = new MenuManager();
addStatusAction(menuManager);
-
- if (!copilotStatusManager.getCopilotStatusResult().isLoading()) {
+
+ if (!Objects.equals(copilotStatusManager.getCopilotStatus(), CopilotStatusResult.LOADING)) {
menuManager.add(new Separator());
addSignInOrSignOutAction(menuManager);
}
@@ -47,14 +49,14 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
}
private void addStatusAction(MenuManager menuManager) {
- String signInStatus = getSignInStatusBasedOnAuthResult(copilotStatusManager.getCopilotStatusResult());
+ String signInStatus = getSignInStatusBasedOnAuthResult(copilotStatusManager.getCopilotStatus());
String signInStatusTitle = Messages.menu_signInStatus + ": " + signInStatus;
MenuActionFactory.createMenuAction(menuManager, signInStatusTitle, handlerService, signInStatus, false);
}
- private String getSignInStatusBasedOnAuthResult(CopilotStatusResult copilotStatusResult) {
- switch (copilotStatusResult.getStatus()) {
+ private String getSignInStatusBasedOnAuthResult(String copilotStatus) {
+ switch (copilotStatus) {
case CopilotStatusResult.OK:
return Messages.menu_signInStatus_ready;
case CopilotStatusResult.ERROR:
@@ -73,7 +75,7 @@ private String getSignInStatusBasedOnAuthResult(CopilotStatusResult copilotStatu
}
private void addSignInOrSignOutAction(MenuManager menuManager) {
- if (copilotStatusManager.getCopilotStatusResult().isSignedIn()) {
+ if (Objects.equals(copilotStatusManager.getCopilotStatus(), CopilotStatusResult.OK)) {
ImageDescriptor signInIcon = UiUtils.buildImageDescriptorFromPngPath("/icons/signin.png");
MenuActionFactory.createMenuAction(menuManager, Messages.menu_signOutFromGitHub, signInIcon, handlerService,
"com.microsoft.copilot.eclipse.commands.signOut", true);
From c014e577ef1b1fa381463abaa1e51b11ab9cc833 Mon Sep 17 00:00:00 2001
From: yanshudan <1397370237@qq.com>
Date: Mon, 23 Dec 2024 10:58:57 +0800
Subject: [PATCH 031/690] feat - Show logs in platform log view (#43)
Co-authored-by: Huiquan Jiang
---
.../META-INF/MANIFEST.MF | 1 +
.../copilot/eclipse/core/CopilotCore.java | 6 ++++--
.../copilot/eclipse/core/CopilotStatusManager.java | 3 ++-
.../core/completion/CompletionCollection.java | 7 +++++--
.../eclipse/core/completion/CompletionJob.java | 4 +++-
.../core/completion/CompletionProvider.java | 3 ++-
.../core/logger/CopilotForEclipseLogger.java | 1 -
.../eclipse/core/{enums => logger}/LogLevel.java | 2 +-
.../logger/handlers/EclipseConsoleHandler.java | 3 +--
.../core/lsp/LsStreamConnectionProvider.java | 3 ++-
.../microsoft/copilot/eclipse/ui/CopilotUi.java | 10 +++++++---
.../eclipse/ui/completion/CompletionHandler.java | 14 ++++++++------
.../eclipse/ui/completion/CompletionManager.java | 4 ++--
.../eclipse/ui/dialogs/SignInConfirmDialog.java | 6 ++++--
.../ui/handlers/ShowStatusBarMenuHandler.java | 4 +++-
.../copilot/eclipse/ui/handlers/SignInHandler.java | 5 +++--
.../eclipse/ui/handlers/SignOutHandler.java | 5 +++--
.../copilot/eclipse/ui/utils/UiUtils.java | 6 ++++--
18 files changed, 55 insertions(+), 32 deletions(-)
rename com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/{enums => logger}/LogLevel.java (83%)
diff --git a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
index b65dbb85..fccf9829 100644
--- a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
@@ -5,6 +5,7 @@ Bundle-SymbolicName: com.microsoft.copilot.eclipse.core;singleton:=true
Bundle-Version: 0.1.0.qualifier
Export-Package: com.microsoft.copilot.eclipse.core,
com.microsoft.copilot.eclipse.core.completion,
+ com.microsoft.copilot.eclipse.core.logger,
com.microsoft.copilot.eclipse.core.lsp,
com.microsoft.copilot.eclipse.core.lsp.protocol,
com.microsoft.copilot.eclipse.core.utils
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
index 844e0c01..bda5ecf2 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
@@ -12,6 +12,7 @@
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
import com.microsoft.copilot.eclipse.core.logger.CopilotForEclipseLogger;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
/**
@@ -58,9 +59,10 @@ void init() {
LanguageServersRegistry.LanguageServerDefinition serverDef = LanguageServersRegistry.getInstance()
.getDefinition(CopilotLanguageServerConnection.SERVER_ID);
if (serverDef == null) {
- // TODO: log & send telemetry
- throw new IllegalStateException(
+ var ex = new IllegalStateException(
"Language server definition not found for " + CopilotLanguageServerConnection.SERVER_ID);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, ex);
+ throw ex;
}
LanguageServerWrapper wrapper = LanguageServiceAccessor.startLanguageServer(serverDef);
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
index 06c5c5cf..d1936eb7 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
@@ -4,6 +4,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
import com.microsoft.copilot.eclipse.core.lsp.protocol.SignInInitiateResult;
@@ -82,7 +83,7 @@ public void checkStatus() {
statusFuture.orTimeout(CHECK_STATUS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS).thenAccept(result -> {
this.copilotStatusResult = result;
}).exceptionally(ex -> {
- // TODO: log & send telemetry
+ CopilotCore.LOGGER.log(LogLevel.ERROR, ex);
this.copilotStatusResult.setStatus(CopilotStatusResult.ERROR);
return null;
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollection.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollection.java
index e3a39533..bc1ad2f6 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollection.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollection.java
@@ -4,6 +4,8 @@
import org.eclipse.jdt.annotation.NonNull;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
/**
@@ -20,8 +22,9 @@ public class CompletionCollection {
*/
public CompletionCollection(@NonNull List completions, String uriString) {
if (completions == null || completions.isEmpty()) {
- throw new IllegalArgumentException("completions cannot be null or empty");
- // TODO: log & send telemetry
+ var ex = new IllegalArgumentException("completions cannot be null or empty");
+ CopilotCore.LOGGER.log(LogLevel.ERROR, ex);
+ throw ex;
}
this.completions = completions;
this.uriString = uriString;
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java
index 5b1972c0..c5933268 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java
@@ -9,6 +9,8 @@
import org.eclipse.core.runtime.jobs.Job;
import com.microsoft.copilot.eclipse.core.Constants;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
@@ -56,7 +58,7 @@ protected IStatus run(IProgressMonitor monitor) {
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
} catch (ExecutionException e) {
- // TODO: log & send telemetry
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
return new Status(IStatus.ERROR, Constants.PLUGIN_ID, e.getMessage(), e);
}
if (monitor.isCanceled()) {
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
index 4ee87467..38082366 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
@@ -9,7 +9,9 @@
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.lsp4j.Position;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionDocument;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
@@ -91,7 +93,6 @@ public void done(IJobChangeEvent event) {
IStatus jobStatus = this.completionJob.getResult();
if (jobStatus != null && !jobStatus.isOK()) {
return;
- // TODO: log & send telemetry
}
CompletionResult result = this.completionJob.getCompletionResult();
if (result == null || result.getCompletions() == null || result.getCompletions().isEmpty()) {
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/CopilotForEclipseLogger.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/CopilotForEclipseLogger.java
index c1751613..44ef6738 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/CopilotForEclipseLogger.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/CopilotForEclipseLogger.java
@@ -8,7 +8,6 @@
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
-import com.microsoft.copilot.eclipse.core.enums.LogLevel;
import com.microsoft.copilot.eclipse.core.logger.handlers.EclipseConsoleHandler;
/**
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/enums/LogLevel.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/LogLevel.java
similarity index 83%
rename from com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/enums/LogLevel.java
rename to com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/LogLevel.java
index f7de0772..ba279c69 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/enums/LogLevel.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/LogLevel.java
@@ -1,4 +1,4 @@
-package com.microsoft.copilot.eclipse.core.enums;
+package com.microsoft.copilot.eclipse.core.logger;
/**
* The event type enum.
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/handlers/EclipseConsoleHandler.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/handlers/EclipseConsoleHandler.java
index c0acb133..07aa0e4e 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/handlers/EclipseConsoleHandler.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/handlers/EclipseConsoleHandler.java
@@ -8,7 +8,7 @@
import org.eclipse.core.runtime.Status;
import com.microsoft.copilot.eclipse.core.Constants;
-import com.microsoft.copilot.eclipse.core.enums.LogLevel;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
/**
* The log appender to send info and error messages to the Eclipse console.
@@ -37,7 +37,6 @@ public void publish(LogRecord logRecord) {
}
LogLevel lvl = (LogLevel) property[0];
Object[] params = (Object[]) property[1];
- // TODO: do we need to add objects in the log message?
int level = map2StatusLevel(lvl);
logger.log(new Status(level, Constants.PLUGIN_ID, getFormatedMessage(params), getThrowable(params)));
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
index 89e79fee..92ce2738 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
@@ -17,6 +17,7 @@
import org.osgi.framework.Bundle;
import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotCapabilities;
import com.microsoft.copilot.eclipse.core.lsp.protocol.InitializationOptions;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NameAndVersion;
@@ -99,7 +100,7 @@ public void start() throws IOException {
try {
return URIUtil.toFile(URIUtil.toURI(FileLocator.toFileURL(url))).toPath();
} catch (URISyntaxException | IOException e) {
- // TODO: Log exception via telemetry.
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
return null;
}
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
index cda7ecd1..b5dc6d23 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
@@ -9,6 +9,8 @@
import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.completion.CompletionListener;
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
+import com.microsoft.copilot.eclipse.core.logger.CopilotForEclipseLogger;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.ui.completion.CompletionStatusListener;
import com.microsoft.copilot.eclipse.ui.completion.EditorLifecycleListener;
@@ -26,6 +28,7 @@ public class CopilotUi extends Plugin {
private CompletionStatusListener completionStatusListener;
private EditorLifecycleListener editorLifecycleListener;
private EditorsManager editorsManager;
+ public static final CopilotForEclipseLogger LOGGER = new CopilotForEclipseLogger(CopilotCore.class.getName());
/**
* Creates the Copilot ui plugin. The plugin is created automatically by the Eclipse framework. Clients must not call
@@ -53,8 +56,9 @@ public void start(BundleContext context) throws Exception {
Thread.sleep(1000);
}
if (connection == null) {
- // TODO: log & send telemetry
- throw new IllegalStateException("Copilot language server is not ready.");
+ var ex = new IllegalStateException("Failed to start copilot language server.");
+ LOGGER.log(LogLevel.ERROR, ex);
+ throw ex;
}
this.editorsManager = new EditorsManager(connection, CopilotCore.getPlugin().getCompletionProvider());
@@ -105,7 +109,7 @@ private void unregisterPartListener() {
window.getPartService().removePartListener(this.editorLifecycleListener);
}
}
-
+
private void unregisterCompletionListener() {
CopilotCore.getPlugin().getCompletionProvider().removeCompletionListener(this.completionStatusListener);
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
index 9eac539e..8717d56d 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
@@ -19,7 +19,9 @@
import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
@@ -50,23 +52,23 @@ public CompletionHandler(CopilotLanguageServerConnection lsConnection, Completio
// if the text viewer is null, we will not register listeners.
// the side effect is that the completion will not be triggered for this editor.
if (textViewer == null) {
- // TODO: log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.INFO, "Text viewer is null for editor: " + editor.getTitle());
return;
}
this.document = LSPEclipseUtils.getDocument(editor);
if (this.document == null) {
- // TODO: log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.INFO, "Document is null for editor: " + editor.getTitle());
return;
}
this.documentUri = LSPEclipseUtils.toUri(document);
if (this.documentUri == null) {
- // TODO: log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.INFO, "Document URI is null for editor: " + editor.getTitle());
return;
}
try {
lsConnection.connectDocument(this.document);
} catch (IOException e) {
- // TODO: log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.ERROR, e);
return;
}
this.documentVersion = -1;
@@ -100,7 +102,7 @@ public void acceptFullSuggestion() {
this.completionManager.acceptSuggestion();
this.document.removePosition(this.triggerPosition);
} catch (BadLocationException e) {
- // TODO: log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.ERROR, e);
return;
}
this.clearCompletionRendering();
@@ -187,7 +189,7 @@ public void dispose() {
try {
this.document.removePositionCategory(this.getCategory());
} catch (BadPositionCategoryException e) {
- // TODO: log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.ERROR, e);
}
this.document.removePositionUpdater(this.positionUpdater);
if (this.textViewer != null) {
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
index 6c812660..a2bf110b 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
@@ -21,6 +21,7 @@
import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
import com.microsoft.copilot.eclipse.core.completion.CompletionListener;
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyShownParams;
@@ -77,8 +78,7 @@ public void triggerCompletion(Position position, int documentVersion) {
this.provider.triggerCompletion(documentUri.toASCIIString(),
LSPEclipseUtils.toPosition(position.getOffset(), this.document), documentVersion);
} catch (BadLocationException e) {
- CopilotUi.getPlugin().getLog().info("triggerCompletion BadLocationException e 77");
- // TODO log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.ERROR, e);
}
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
index 417ef6d0..c5558fc1 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
@@ -12,7 +12,9 @@
import org.eclipse.swt.widgets.Shell;
import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
+import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.i18n.Messages;
/**
@@ -54,7 +56,7 @@ public void run() {
try {
this.run(true, true, task);
} catch (Exception e) {
- // TODO: log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.ERROR, e);
}
}
@@ -75,7 +77,7 @@ public void run(IProgressMonitor monitor) throws InvocationTargetException, Inte
try {
return CopilotCore.getPlugin().getCopilotStatusManager().signInConfirm(userCode);
} catch (Exception e) {
- // TODO: log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.ERROR, e);
return null;
}
});
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
index 7382e60a..7753b768 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
@@ -17,7 +17,9 @@
import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
+import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.i18n.Messages;
import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
@@ -95,7 +97,7 @@ public void run() {
try {
handlerService.executeCommand(commandId, null);
} catch (Exception e) {
- // TODO: log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.ERROR, e);
}
}
};
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
index 358afaaf..d3c18e50 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
@@ -12,7 +12,9 @@
import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.protocol.SignInInitiateResult;
+import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.dialogs.SignInConfirmDialog;
import com.microsoft.copilot.eclipse.ui.dialogs.SignInDialog;
import com.microsoft.copilot.eclipse.ui.i18n.Messages;
@@ -48,7 +50,6 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
}
} catch (Exception e) {
handleSignInException(shell, e);
- // TODO log & send telemetry
}
return null;
@@ -106,7 +107,7 @@ private void handleSignInException(Shell shell, Exception e) {
String msg = Messages.signInHandler_msgDialog_signInFailed;
if (StringUtils.isNotBlank(e.getMessage())) {
msg += " " + e.getMessage();
- // TODO log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.ERROR, msg, e);
}
MessageDialog.openError(shell, Messages.signInHandler_msgDialog_signInFailedFailure, msg);
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
index 3c1c0971..54f0b5b4 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
@@ -9,7 +9,9 @@
import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
+import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.i18n.Messages;
import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
@@ -38,7 +40,6 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
}
} catch (Exception e) {
handleSignOutException(shell, e);
- // TODO: log & send telemetry
}
return null;
@@ -48,7 +49,7 @@ private void handleSignOutException(Shell shell, Exception e) {
String msg = Messages.signOutHandler_msgDialog_signOutFailed;
if (StringUtils.isNotBlank(e.getMessage())) {
msg += " " + e.getMessage();
- // TODO: log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.ERROR, e);
}
MessageDialog.openError(shell, Messages.signOutHandler_msgDialog_signOutFailedFailure, msg);
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
index 4df81a79..6fc1e0ed 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
@@ -18,7 +18,9 @@
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
import org.eclipse.ui.texteditor.ITextEditor;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.utils.PlatformUtils;
+import com.microsoft.copilot.eclipse.ui.CopilotUi;
/**
* Utilities for Eclipse UI.
@@ -53,7 +55,7 @@ public static boolean openLink(String link) {
IWebBrowser browser = browserSupport.createBrowser(IWorkbenchBrowserSupport.AS_EXTERNAL, null, null, null);
browser.openURL(new URI(encodedUrl).toURL());
} catch (Exception e) {
- // TODO: log & send telemetry
+ CopilotUi.LOGGER.log(LogLevel.ERROR, e);
return false;
}
return true;
@@ -91,7 +93,7 @@ public static int getCaretOffset(ITextViewer textViewer) {
public static int modelOffset2WidgetOffset(ITextViewer textViewer, int offset) {
return textViewer instanceof ITextViewerExtension5 extension ? extension.modelOffset2WidgetOffset(offset) : offset;
}
-
+
/**
* Builds an image descriptor from a PNG file at the given path.
*/
From 1e266a09fd83893fcd5947c1594859134c0bb257 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Tue, 24 Dec 2024 09:43:09 +0800
Subject: [PATCH 032/690] fix - NPE when close a read-only editor (#51)
* When editor is readonly, members of CompletionHandler may be null. Should
take this into consideration when disposing it.
---
.../ui/completion/CompletionHandler.java | 23 ++++++++++---------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
index 8717d56d..165ec81e 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
@@ -181,10 +181,13 @@ private String getCategory() {
* Disposes the resources of this completion handler.
*/
public void dispose() {
- if (this.completionManager != null) {
- this.completionManager.dispose();
- this.completionManager = null;
+ if (this.completionManager == null) {
+ // null manager means the handler is not initialized.
+ return;
}
+
+ this.completionManager.dispose();
+ this.completionManager = null;
lsConnection.disconnectDocument(this.documentUri);
try {
this.document.removePositionCategory(this.getCategory());
@@ -192,14 +195,12 @@ public void dispose() {
CopilotUi.LOGGER.log(LogLevel.ERROR, e);
}
this.document.removePositionUpdater(this.positionUpdater);
- if (this.textViewer != null) {
- SwtUtils.invokeOnDisplayThread(() -> {
- if (this.textViewer.getTextWidget() != null) {
- this.textViewer.getTextWidget().removeCaretListener(this);
- }
- this.textViewer.removeTextListener(this);
- });
- }
+ SwtUtils.invokeOnDisplayThread(() -> {
+ if (this.textViewer.getTextWidget() != null) {
+ this.textViewer.getTextWidget().removeCaretListener(this);
+ }
+ this.textViewer.removeTextListener(this);
+ });
}
From 2ec00bedfa095cff67d0992adfe13b0275d74c24 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Tue, 24 Dec 2024 11:02:34 +0800
Subject: [PATCH 033/690] fix - Update the cursor position from event (#49)
* fix - Update the cursor position from event and rename variable
---
.../core/completion/CompletionProvider.java | 2 --
.../eclipse/ui/completion/CompletionHandler.java | 4 ++--
.../copilot/eclipse/ui/utils/UiUtils.java | 16 +++++++---------
3 files changed, 9 insertions(+), 13 deletions(-)
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
index 38082366..0743cdb7 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
@@ -9,9 +9,7 @@
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.lsp4j.Position;
-import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
-import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionDocument;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
index 165ec81e..7fdb2974 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
@@ -139,8 +139,8 @@ public CompletionCollection getCompletions() {
@Override
public void caretMoved(CaretEvent event) {
- int caretOffset = UiUtils.getCaretOffset(this.textViewer);
- this.triggerPosition = new org.eclipse.jface.text.Position(caretOffset);
+ int modelOffset = UiUtils.widgetOffset2ModelOffset(textViewer, event.caretOffset);
+ this.triggerPosition = new org.eclipse.jface.text.Position(modelOffset);
// it's guaranteed that the document change event comes earlier than caret
// change event. See org.eclipse.swt.custom.StyledText#modifyContent()
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
index 6fc1e0ed..37b7d18d 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
@@ -77,21 +77,19 @@ public static ImageDescriptor resizeIcon(String path, int width, int height) {
}
/**
- * Gets the caret offset of the given text viewer.
+ * Returns the widget offset that corresponds to the given offset in the viewer's input document or -1 if
+ * there is no such offset.
*/
- public static int getCaretOffset(ITextViewer textViewer) {
- if (textViewer == null) {
- return 0;
- }
- return textViewer.getSelectedRange().x;
+ public static int modelOffset2WidgetOffset(ITextViewer textViewer, int offset) {
+ return textViewer instanceof ITextViewerExtension5 extension ? extension.modelOffset2WidgetOffset(offset) : offset;
}
/**
- * Returns the widget offset that corresponds to the given offset in the viewer's input document or -1 if
+ * Returns the offset of the viewer's input document that corresponds to the given widget offset or -1 if
* there is no such offset.
*/
- public static int modelOffset2WidgetOffset(ITextViewer textViewer, int offset) {
- return textViewer instanceof ITextViewerExtension5 extension ? extension.modelOffset2WidgetOffset(offset) : offset;
+ public static int widgetOffset2ModelOffset(ITextViewer textViewer, int offset) {
+ return textViewer instanceof ITextViewerExtension5 extension ? extension.widgetOffset2ModelOffset(offset) : offset;
}
/**
From a7fa37158a80aaac1a260a8ca0bf014d2902885a Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Tue, 24 Dec 2024 11:03:12 +0800
Subject: [PATCH 034/690] fix - Remove unused listener (#52)
---
.../eclipse/ui/completion/CompletionHandler.java | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
index 7fdb2974..b5962e63 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
@@ -7,10 +7,8 @@
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DefaultPositionUpdater;
import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextViewer;
-import org.eclipse.jface.text.TextEvent;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.swt.custom.CaretEvent;
@@ -29,7 +27,7 @@
* A class to listen events which are completion related and notify the completion manager to render the ghost text or
* apply the suggestion to document.
*/
-public class CompletionHandler implements ITextListener, CaretListener {
+public class CompletionHandler implements CaretListener {
private CopilotLanguageServerConnection lsConnection;
private CompletionProvider provider;
@@ -114,7 +112,6 @@ public void acceptFullSuggestion() {
void registerListeners() {
SwtUtils.invokeOnDisplayThread(() -> {
this.textViewer.getTextWidget().addCaretListener(this);
- this.textViewer.addTextListener(this);
});
}
@@ -162,14 +159,6 @@ public void caretMoved(CaretEvent event) {
}
- @Override
- public void textChanged(TextEvent event) {
- // this event comes earlier than caret change event. So we should check if the typed characters
- // are the same as the ghost. Then determine whether a re-redering or a new completion
- // request is needed.
- // TODO: check changed text
- }
-
/**
* Get category for the position updater of this document.
*/
@@ -199,7 +188,6 @@ public void dispose() {
if (this.textViewer.getTextWidget() != null) {
this.textViewer.getTextWidget().removeCaretListener(this);
}
- this.textViewer.removeTextListener(this);
});
}
From 4c8dccc2466e3aeb8481bbf7352e49215b013ed0 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Tue, 24 Dec 2024 16:21:53 +0800
Subject: [PATCH 035/690] fix - Typo fix (#62)
---
.../copilot/eclipse/core/lsp/LsStreamConnectionProvider.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
index 92ce2738..50728ac2 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
@@ -30,7 +30,7 @@ public class LsStreamConnectionProvider extends ProcessStreamConnectionProvider
public static final String EDITOR_NAME = "Eclipse";
- public static final String EDITOR_PLUGIN_NAME = "GotHub Copilot for Eclipse";
+ public static final String EDITOR_PLUGIN_NAME = "GitHub Copilot for Eclipse";
@Override
public Object getInitializationOptions(@Nullable URI rootUri) {
From 17db215303934559f61aa829107cb6f2632c137e Mon Sep 17 00:00:00 2001
From: ethanyhou <149548697+ethanyhou@users.noreply.github.com>
Date: Tue, 24 Dec 2024 16:52:35 +0800
Subject: [PATCH 036/690] feat - Enabled completion loading spinner while
copilot is completing document. (#44)
---
.../core/CopilotStatusManagerTests.java | 10 +-
.../eclipse/core/CopilotStatusManager.java | 10 +-
.../core/completion/CompletionProvider.java | 26 +++-
.../completion/CompletionStatusListener.java | 18 +++
.../icons/spinner/1.png | Bin 0 -> 481 bytes
.../icons/spinner/1@2x.png | Bin 0 -> 1603 bytes
.../icons/spinner/2.png | Bin 0 -> 466 bytes
.../icons/spinner/2@2x.png | Bin 0 -> 1670 bytes
.../icons/spinner/3.png | Bin 0 -> 464 bytes
.../icons/spinner/3@2x.png | Bin 0 -> 1684 bytes
.../icons/spinner/4.png | Bin 0 -> 462 bytes
.../icons/spinner/4@2x.png | Bin 0 -> 1640 bytes
.../icons/spinner/5.png | Bin 0 -> 467 bytes
.../icons/spinner/5@2x.png | Bin 0 -> 1659 bytes
.../icons/spinner/6.png | Bin 0 -> 465 bytes
.../icons/spinner/6@2x.png | Bin 0 -> 1763 bytes
.../icons/spinner/7.png | Bin 0 -> 465 bytes
.../icons/spinner/7@2x.png | Bin 0 -> 1724 bytes
.../icons/spinner/8.png | Bin 0 -> 465 bytes
.../icons/spinner/8@2x.png | Bin 0 -> 1686 bytes
.../plugin.properties | 3 +-
com.microsoft.copilot.eclipse.ui/plugin.xml | 8 +
.../copilot/eclipse/ui/CopilotUi.java | 23 +--
.../copilot/eclipse/ui/UiConstants.java | 5 +
.../completion/CompletionStatusListener.java | 16 --
.../completion/CompletionStatusManager.java | 34 +++++
.../eclipse/ui/handlers/CopilotHandler.java | 5 +
.../ui/handlers/ShowStatusBarMenuHandler.java | 141 ++++++++++++++++--
.../ui/handlers/ViewFeedbackForumHandler.java | 28 ++++
.../copilot/eclipse/ui/i18n/Messages.java | 16 +-
.../eclipse/ui/i18n/messages.properties | 16 +-
.../copilot/eclipse/ui/utils/UiUtils.java | 14 +-
32 files changed, 305 insertions(+), 68 deletions(-)
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionStatusListener.java
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/1.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/1@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/2.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/2@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/3.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/3@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/4.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/4@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/5.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/5@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/6.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/6@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/7.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/7@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/8.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/spinner/8@2x.png
delete mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusListener.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusManager.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ViewFeedbackForumHandler.java
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java
index 2286368d..dabf9401 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java
@@ -39,24 +39,18 @@ void testCopilotStatusResultOnSuccess() {
}
@Test
- void testCheckStatusLoadingWithDelay() throws InterruptedException {
+ void testCheckStatusOK() throws InterruptedException {
String mockedUser = "mockedUser";
// Arrange
CopilotStatusResult expectedResult = new CopilotStatusResult();
expectedResult.setStatus(CopilotStatusResult.OK);
expectedResult.setUser(mockedUser);
CompletableFuture future = new CompletableFuture<>();
-
when(mockConnection.checkStatus(false)).thenReturn(future);
+ future.complete(expectedResult);
- // Act
copilotStatusManager.checkStatus();
- // Assert initial status is LOADING
- assertEquals(CopilotStatusResult.LOADING, copilotStatusManager.getCopilotStatus());
-
- future.complete(expectedResult);
-
// Assert final status is OK
assertEquals(CopilotStatusResult.OK, copilotStatusManager.getCopilotStatus());
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
index d1936eb7..ca0e6049 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
@@ -28,7 +28,7 @@ public class CopilotStatusManager {
public CopilotStatusManager(CopilotLanguageServerConnection connection) {
this.connection = connection;
this.copilotStatusResult = new CopilotStatusResult();
- this.copilotStatusResult.setStatus(CopilotStatusResult.LOADING);
+ this.copilotStatusResult.setStatus(CopilotStatusResult.OK);
}
/**
@@ -73,6 +73,14 @@ public CopilotStatusResult signOut() throws InterruptedException, ExecutionExcep
}
return result;
}
+
+ /**
+ * Set the status to OK.
+ */
+ public CopilotStatusResult setCompletionDone() {
+ this.copilotStatusResult.setStatus(CopilotStatusResult.OK);
+ return this.copilotStatusResult;
+ }
/**
* Check the login status for current machine.
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
index 0743cdb7..9da84e08 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
@@ -23,6 +23,7 @@ public class CompletionProvider implements IJobChangeListener {
private CompletionJob completionJob;
private Set completionListeners;
+ private Set completionStatusListeners;
private CopilotStatusManager statusManager;
/**
@@ -33,6 +34,7 @@ public CompletionProvider(CopilotLanguageServerConnection lsConnection, CopilotS
this.completionJob = new CompletionJob(lsConnection);
this.completionJob.addJobChangeListener(this);
this.completionListeners = new LinkedHashSet<>();
+ this.completionStatusListeners = new LinkedHashSet<>();
}
/**
@@ -66,6 +68,13 @@ public void triggerCompletion(String uriString, Position position, int documentV
public void addCompletionListener(CompletionListener listener) {
this.completionListeners.add(listener);
}
+
+ /**
+ * Register a completion status listener.
+ */
+ public void addCompletionStatusListener(CompletionStatusListener listener) {
+ this.completionStatusListeners.add(listener);
+ }
/**
* Remove a completion listener.
@@ -73,11 +82,20 @@ public void addCompletionListener(CompletionListener listener) {
public void removeCompletionListener(CompletionListener listener) {
this.completionListeners.remove(listener);
}
+
+ /**
+ * Unregister a completion status listener.
+ */
+ public void removeCompletionStatusListener(CompletionStatusListener listener) {
+ listener.onCompletionDone();
+ this.completionStatusListeners.remove(listener);
+ }
@Override
public void aboutToRun(IJobChangeEvent event) {
- // do nothing
-
+ for (CompletionStatusListener listener : this.completionStatusListeners) {
+ listener.onCompletionAboutToRun();
+ }
}
@Override
@@ -88,6 +106,10 @@ public void awake(IJobChangeEvent event) {
@Override
public void done(IJobChangeEvent event) {
+ for (CompletionStatusListener listener : this.completionStatusListeners) {
+ listener.onCompletionDone();
+ }
+
IStatus jobStatus = this.completionJob.getResult();
if (jobStatus != null && !jobStatus.isOK()) {
return;
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionStatusListener.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionStatusListener.java
new file mode 100644
index 00000000..267b5c1b
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionStatusListener.java
@@ -0,0 +1,18 @@
+package com.microsoft.copilot.eclipse.core.completion;
+
+/**
+ * Listener for completion status to track the completion progress.
+ */
+public interface CompletionStatusListener {
+
+ /**
+ * Notifies to the listeners when the completion is about to run.
+ */
+ void onCompletionAboutToRun();
+
+ /**
+ * Notifies to the listeners when the completion is done.
+ */
+ void onCompletionDone();
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/icons/spinner/1.png b/com.microsoft.copilot.eclipse.ui/icons/spinner/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..2147f798fbde3cac160ba00f6ad41272e4726ee7
GIT binary patch
literal 481
zcmV<70UrK|P)#a3TjHd4N;h%Rf*o8OijyLg
zf+&gz#cZ`xJg?r!GfkVjB;=g;&iyVor6frxua3?-7R{?s{57QhQ@?(Gu-D4-DKxEg
zHQL$Ur1i=Y%`GlO(`==*10>dv7;vIfu9h#v;pdkDbsu}Qv07P%Zm`}(NALeT(A7Xn
z3`E&6C(XDk@zyCBLa;*^4Uc*F@W3
z^O&tsbb)9aev|!ec@MV0<}#a)S=?sW*PvUhx1c{{_rR6d+#GgG(yhWhK%#*}n-fE9
zCKc_04>p(Cd@`!GNECg{HUby*LG{y_M8?rsheeq+loWCMN#Sn3&kaMBj(FIRD*~kn2MxLBE^W-d!n}YGja{iCU=n>We@lOCb8z!FKL}3qH0FcEM
zQjVNLF-@-cn)bfrH~7xzqXR2O!~{NCr}vZHjU0XPKLUP3sDr^r@b~28-5rDR3E9Aj
zf7f~G%~^AuqpO;H@eL;b$)^_=rJqCrX@m(--%ZFp0rVE88^=>qe{K#Zugi$H`MA8l
zi+-P(XX3;>i=S!`Mu5&O<}HZa#FI-3IWJu495|O|dXwoIH%(V|lITnZt{ONZJC2=g
z$&hysjmzauDi?Q9NtwgZe1-Th-RT{?sA&zJ(bT%AM(c>aF)z6#K#CM{PB@1thQ-qe
zOV4P!)Irn5?UC&|k5E|KP48B@sB8#~$U1IBF0oTtW1~}QtN1Dth^<3%2;enb^Z3Yw
z2Ep{4(Udu;CbucRW}fH;8w#-S#DKpu0^Ea_(m;~VYa_MzUh5*ca?SbCq0rVE%@QTBW_
zM5FGQee@vh>y^Fd=qs}_0NbFkQca=lJ_QNnKY+RXV)9eAU-&xhg6J!p;;;E_;&%)7
z3SKBg5&8hx+Vk<}8f*WUlKwrdG=HIm__L8C>AG&3)^^f#i9`Hx(S`*gu=;DgR8tA$
zr4DiL;_TKZ*s8jsLr~qGCA(%9U4PI^HtSv(s9dOgkC7J%IvkF5ys&zuOidRY
zd$bWv%|{M^hg4&B{!pl-WA6<`TatrG3b`jeCLs&Hb0F$Q8xomVuWw~s?wlKAHz
zQ-8f2PFzn71HkE0`)BNTS+^(dI|v8{gAd8F{CGHVgF!TKF$V4qa}J#Ou)#-C-NP63
zNRF_D&ZnUIQzdrNvwQ*KUOlAyP;X*ahTJ_k&XhYSQ);K$oO)kmOHDVqf)VTeBPsCq
z6kIB)eKzs3#+rO#*<0Wpd7{=$4ApMRG+o<`icB)k|Gvsro4jK`2fU3~jmrBeAg~#!j
zS8>s&7I}2&Q8*nE!srtuxPI-6B{%yc6?mUy(BKow7FaaG01}&dde}GknRKWpAyv7l
zRBm6m2s0`zO!Na|_aHCG9-fG1Cu72*u2l4NVspsh=jL)hGtPh^&3dW_+i3yRwy}$?O+ejb6I5Pq?B)
zU)c%Kw~3a87Lj0>u6_v`5~Bu@sEdaLO
zL)ra6Ug8v+7uQVq#0sojY^7%VgKz}XjpN(5Z?6s~Z-fx=T@ccC(azaT4au!>QT2V>
z@kLoH?5_dR&4$jso*{P+jfX0oGz4Px5B%Fu2Q5~eN9?ceq9H>d8EW8@A!yG-`|RXB
z)qE}YNOtZn8x5(fG54D-;-;mn^$$)5W)yC3mhS)n002ovPDHLkV1m;?
B5CH%H
literal 0
HcmV?d00001
diff --git a/com.microsoft.copilot.eclipse.ui/icons/spinner/2.png b/com.microsoft.copilot.eclipse.ui/icons/spinner/2.png
new file mode 100644
index 0000000000000000000000000000000000000000..ac886be1fa6a5cff819fda43f0c718f16ab85d3a
GIT binary patch
literal 466
zcmV;@0WJQCP){b;%#_&Ce2*}IVU;id)%jGSq5cu(HkB?(+Ja_8H``Y?)BN}O_@)Jsxgi5
z_;4Tgb~a&cV?C_0HKarf69Z1{wAwAWe;UF2WD1ML>8z00|6|U%{mZ+8{_h7=`{VN~AQ2NgDl0WYJ!IWozSqvL9uAPrW411`
zJ;I8?v)DqvV1J?`nwwhuI8GC;rOIRC3xeC39k2<@*(20N5oa+b*ko*#H0l07*qo
IM6N<$g0bPtyZ`_I
literal 0
HcmV?d00001
diff --git a/com.microsoft.copilot.eclipse.ui/icons/spinner/2@2x.png b/com.microsoft.copilot.eclipse.ui/icons/spinner/2@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..60b04c0449775bb9eedc0408e5f57f9ac252aff8
GIT binary patch
literal 1670
zcmV;126_33P)ga|%%
z=FTjS!D6A<7E}AfQ0)4nKl;=3U(>E>p%PFDCQbiM&ski>RFIcWa>AWEm-~J9+;hHj
zHY#cXPf26r3LV2)3=BDMVEHy2J6cv!bLUV9t%z+8WmO4R
zalXubZY})J5STp8t`$4EHBSXMFnseAFgasjxe9}uZ^U;bHFYY!TTAmHdqYoA1iZHC
z+t7Xo38h#_3wlORb2lG=4#6JY2VM)mM?Ck$C*SS=sL0I+@qVYz>sV5cBuYHo?jk3@
zOI^$Vno~_eT2EI~hQJRsNWkFn8*yE_!@n)fA@->NSYZ)DiDuFrM{%3NdsOj9
zb8Cp~I^AD4GB&Yucyuy8bbCTs*Y+KU1avH4HY0^1lxL4t?szq;PsF%*oLy`eX#
z1CT8NJ9C~JRn4<%H@n}qfxli*TVXVC&wGy_eH80|Wm)Zw_C7T;JZQS(AJ
zpeU8l{+&8nh%KSX)-rGVYg3{F;s9!OR?l!!xVV7fxP&1s6vM4r3UH9`JtZtr>KK0#
zreS29?d6uEwpSw{s9TPvxA!G9o3Go0bH(6^#S+kgwjiK^7PkPI7jdro)TOOBSHy^)
zMxo+Cex5>L{5U!~v8gdIC>6LkDX*5)FP4D8&BfqpBj6(=@^aW;Spb|~v3MezGU>;n}YTcaVZ0bu#
za5RgrPfD#MlG4JP7hP37+{eY>?s9KnV+7pQIG{|m6Q$A?h>6N`3L8KmNH&~U%Scjz
zTarMCB+zwERYl%eEP-@K>oT>IOUSM!N;RBX!*+$iRaP;1H}n$WMHxT>CfO7_&nqnC
zLLw!)e7`Yek8R
zd{BINo}R=$084Vjpc0nPJNj9ofWF`$idtCYAkF8Nw=`FYph!Z0*slU`bL4%_eWP1U
zjeuerK76N1-s~N()4#3IJp~rY+psHHLWR8nu`-vgxd>0q^7$0Uz4EA)^CuyRBKK6^
z@Wdyxl1or
z>);*GlI
z&g(uj%ZVPJ76_Dj_{~^EpuH;8p;x9ET$LJ$WmwzS?zaQbk?wD|}qSQW{pnB%N4Yjnm9_mL0
zR7(gxiPF##1umwbetZ^dJG=V1{6fyI;@(fIPx#07*qoM6N<$f|aTs+yDRo
literal 0
HcmV?d00001
diff --git a/com.microsoft.copilot.eclipse.ui/icons/spinner/3.png b/com.microsoft.copilot.eclipse.ui/icons/spinner/3.png
new file mode 100644
index 0000000000000000000000000000000000000000..0d8986b9d74be44af3d1064add81bb5a0c45a89e
GIT binary patch
literal 464
zcmV;>0WbcEP)JXe1p%kQ|
zh)~Q{JH>PHMP6Q$<}QJEZr(ZH&5er?=)R)+a|x1Unse;^G4Evh$;E9>pSyr#zrBCDEW)v?vvO?czSP^r|^$>a_iBdhWxd{1L;_-kEQbcb9X%ch0%z
zoF@=WV@M30bBpRYgM#9^6*T99U#rB@86)^VMj9y3tww$MwaBkvxPL)&0}`5j>pv$T
z)N-aQBl7E7d_GAHeoy+fAUi?`6w};NsfEpwTDSoG=hAGpC&MnKxr2m+l;PqYN?>gb
zUbnsmdFfrpH{QJY`{JuN#y$xAdT04|JXlHylvtgGWM7=Y6O*Jv_DmypGN&317Sy^r
zh;p))Tg{pBYfzxR?XdaA?XhJyNAIl&-2OAsw&&P3-U?P!^iTrnRov1w)yP}f+~3>Y
zJM0a?=;-$n%YYkVALYU<6DrECMghJ3(B(b*{FmT-fIwQ$1!NQ#D6w+FNd=_qaa)D~
zdDXUlFa7y(ueSN1H_wds7S!XmNP{?|3SV4Zg|8VMhXakB1A!{Xp+Fv3rJxoCA`+0S
zbL|ppu~!5{2pPEdyQg}MQ?>Y_P>%y3Bam5z0%B8y%0nZ*0wpAAQDL$UrA;@N#8+Zj
zqR!PS)FRE~(J%4MDXMj5K#=H)Db7%A;e@sc5j@jDYKO$aGH?^l$!T-}0-7nad7kDM
zGc{~ft_hh7YLHz{vtbY->ul_^Gp-$G&kH%5(FO2-5Uy$%gh+86`?3K1Bmw`}t6C14
z2`d6Q!%7GVCF5K(^Wx=lKnw*nE&W$W#Vi9t;~ACX^$KD&?5hbH*Vc4BvTwBZ+GT)o|Fcah
zYwt@lCxG67+VLCIj$nR$&wUx)J06G(caM~OeiI>)tizkKOk88mX0CBV9WIqv-O;mk
z4Of)o_fxd46RVBrZtiAux3Crs7F*fHlO9so`kt3tobRZt9n-c#wrTfM#SQ2c68Td)
zsIYKrq3~!zP||GfRF#)Pb(B+VdI^I>1ab|#v>SfEc{|t=MqAq
z%;I^tjCE%ABWNZxb*)EZZ{7MU`p%udV}2POeXVKtmnnpR)XFUiB`PU2t4ao?fyFXZ
zO!soq9h0>jXjTf$NThG}y*oNO`rOsAv8e0U@6Fq>_tXO39gS$gBz2ry1kHrTu+3M#
zXa70%&YmF+f!=;VLo6fP()lJ7t|!)3qa3ImYUmfkAUQ)))Ba4ZzH45#5#H4HMOxQ>
zzi{W?Ga>@Bf=7C
z#N>{KkC7;11)c~qUb@rtC;?=MDw=(w#_j=Ld10+9y|50aM%YZaR6;${KyuP#0vT~N
zE(z!MN>{en=vHrcpHZLw{>rKe-E<*zzp}6l1DBYpLn0A?kjXZ=g;UGn8g?;hMpE=>
z*~Iz83X~_VcB|Wa&hq%?+snyhzQl@h6f{y!+#6Tsi6L^*be{RM_Qe!ej#HDhNL}B4
zB22>R8(ss|vvev~b=m{cb`<3H5^#}P*|=X_@yUU3AU-m{W^0g-lN2q=gZNOxjZi~w
em#U`uW&962Wn#xBu)%Wx0000BEq~1oOIN!jwJSEZN+SmO@
zN}dOlevdlP14R$!8l>qsm)QMho^c(fu{94K`P@^{Rfrwc!t?zhz7}
z5o{Y^6Ot{)UA$`Sp2`kA()U0pY@&+2au1NWjzmW$Ci-R;?LiDSA=zSb8V)D&M>uzM
z_Sqhky+&+alk0LKuX8szxLWv_3T`{$WSN+h`ThhL0Ae5~N-3R0-v9sr07*qoM6N<$
Ef@9~m0N>8CZP)@IuLQ79!AXO}g3M2-#
z=Y8K(3PqGLuXDPHAl5A|*|He7M7LjT;t%^Hmh9|#4=sZwauMc9o`f9UzR%}<-tTic
zad9v46mY`w15R9~bqg&T4?YIQl{tmAaj)W`&hzZ3v#1`27+#n%fDsP6#f|?vK)MMr
zY8wt2j}P~h^1@0^5SD4&h?#`I4NK-|ueE-zzxWl2bzlP6fw8-`@Yt
z8hg)p@Nq4V&rM&uj}umATG94`T4D2ooF`QgU4|L^vTLxfunzke52~E{pFwQRsu2ge
z0HC}7Ug-MW$MsVHa1GP;a4XH?M79lw_d9SHBGfxC=Lz6kVFJglf#cTp%f8||WYX}0
zuN;IX|KA{1DU2j!>9`^TQ0ofbX$OEH6{WnUQf!4a;r#mG{i@bW;X)@q4~GB6jw2X885j$92k(XH9va@?Od+%lIPHZ1d#X`<3Wt5QC$GuQV*uJFVJ8J(
zn*4jCplbobvCoFi=GP!pQjc4xb9oOV*_&KV!p62Mvhx@KbxZ*?Jg#_g_zidq4Zq$y
zJOcoJR7e3d4JdxDD})H`py6klhh_lCnKB7bIO_%(mOx#$75l91Bfi#iw?KfR8t$$Q
zMC6{Yh%-#&;yw_crQ_>xoBuo6DZ}GyV0Y)WL9QKtN!2f(nueaoo`t4yP7S^ddxT~2
zO$zYdf;Ko%C+^mI(1BS+Gsjb0Pf8h{xV1bn&bmK0Dnv9=Y?w
z(@y$M>Gi&$<%M79b;PAqlXA5^C-G1;yDd(*R>8S`83y@e%u4dT+k*@yn2$b!}
zt?-jGa&_g=W3XGa_UES>F=sn@RR*Be>i_1TtOdA=#R9h9N@r!J8FQtc5O>%Uj0zFy8SmhV#RAa&0#W`MdPk(l8GLrw(;pS0@T=a_
z_ueEWK%|*TzS>H3`F5f!a-v*F!&OU_hKUBeDa}YwmJL11cc4emJNgeb3-8ZGzHDzT
zUawq4Jy2T6R>%Nto@4GFGUG`xipiN4u>yt(c7GkwF%3kqkl)Aa`&K0z$ZZI_1VfDk
zC0PQ&jm~Ru-BU%{|t7HJd`$7Qd3ma+p4M#_206<#qq5u?xR>=ZKU}v!lt6)x?G6-)9
zE#olKDN`Z*I`8ld0I2=vDFC!~?HsimvoK7sx+6nuJ04}r8ikK&S*8<>{9RzfTj0Au
zrE3{f(7Ij#4GRj&rxZfRqwVyLGtA;sSRUXoV3JA46sKZ%mFTa!`XM2rmC@lZRsYg
za`oI$#URdKyuX8{>4HwUh}yn9xjK-c0*LbFA(g>9G$q7}#@;ory0|yJ`CvfZA{ajDeK$NL!q~j=)Rc1wmiwvW7W
zw+1#>u=T+9LURUZ;bK1$`%M#UuEge2)!0@f?MJ4{UN!1|jbnq&6>Pn#A8>5!M`FKe
z^7o#8OLQuu5%|kFy>w+&1nZZM|FM$7yubFCw0sg@J0Xt002ov
JPDHLkV1iB@(pdli
literal 0
HcmV?d00001
diff --git a/com.microsoft.copilot.eclipse.ui/icons/spinner/5@2x.png b/com.microsoft.copilot.eclipse.ui/icons/spinner/5@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..66ede48bb22c732e25ab4b108b1c4ba96d63d63f
GIT binary patch
literal 1659
zcmV->288*EP)V6ee>sX4&^*G-g}e2m8=uiPKO7(NaMKp-{0<+NxB_ZD~uTrM>so&%0s6CWu+0
z*!#Zky`>aI*#JMbshc3HS^OZ&68|hqOm-80?#F&?|CX#f?`=!KMXZ3n$w_m2?|q-=
zocEmPoSTy~hb0KYGa5V2&$r|H{AS5gFxxPSVyg%FW47kN+bkybnwIGk%agq7Li
z#f;vE87qtQnR{g(bArC6Ca)_+tUi}%E?$5oPi#7RlOK5HLJK;wwy
z761Wn_eB*vL!dr{-d72vUpDi-ubM+>Ck3TM8d&5q`Q?I-NMie0u7wFffBHUBCDo9Zxn_WH$G7RpbZ}t0s+f`GcyP@aFHrX0FFsF
z>oII}N_CJpOiSkwBlKQotU>V;D=*s3JZj7~i8r;7W2KOMuFkLH9pY6L6q57=L`%-H
z1d*l2r-nRj*@I`+Ru2|w_N&R4O2EFy+Mu>w{Lso{Z9@QmJXb9-_{df>i<>ePG`qGt
zqPdzqQZbyNcB37?z0eApXU9dFCK7@Q`K-VpR~v_h7O6K@8;2G{=2n2&3LMx5N@vr7
zmAWIFjmGpt^H{LQV%j$}c9JM?kiu|v$jLMS8Shx
zHH6}}N-wtQ{bbF&dxu@xRS3Y<_$jx&pT_6aA>}pP>A6HcSFogeGTkQO8gQ|QW
zsa6)-Xcf$tK;>R^0QOIWzPykS&;4{k1&7K4@aq;9oysu=qsvV})JOp>Y-DrF#NAd7
z?=t$xYx7P*PJGK0#NR{DNLYFFIhAe@rEBzhXGqkj4^bV5o3*OfU{Jf;8}{L43sr&*
z99j(?OB7T!P8_xdanb+Tg0?fF_*}Yt7HQj}uP{vNdQRh;GrF?WjSS_jsUAZ2cezf!
z@W>aTDSe>zU@i0no!&1MKiGt_S)eO17)X?|R;)k{D3H`&W%r^@UIi^#SKcP;Y{D5`
zbwJe6WaSTGQm^m~y>x*j{@p&r8Re^q8mprCB-an?J
zGHqr_9wlK5$x9#UfFrUBR8QH?o>q><#uaGCidxn@8lWV=46}u88TH=Nbx{SUrY0#X
zt)K)<{Hc|h(LAq-ctG0+HO*wSfWZRI7uEY_WLV!!l(w$(DtLx~C4_gV1VUol!)3FH
zyjI}EI(x^^3<6Xa)Sk}bH?)n>B>;Q_yi7uvlc4R%Vi!)9dWcftiY})U(FO?rF5&D9
z0&4luW8jfx)({puNg++XK-Y+_6JOOef=*CD%7_v)7J&kr_6;V(_=hBX7bF`GpG{Wo
z#mQtM4aHjAppG<<2_*H!kaQTJ;c8*G+fOR9drv9a%q{kAr1
zBG_E8g@Vn)SwsrE4LuNg3wo^fAe7j`0`@@CgUTMj@jyOT-`X
zd+3LF210Mmdr)>8(Yz7IWk;@aCpfMzy-WqColjIUzXA*ZqQNJHlVLgu00000NkvXX
Hu0mjfPHWL1
literal 0
HcmV?d00001
diff --git a/com.microsoft.copilot.eclipse.ui/icons/spinner/6@2x.png b/com.microsoft.copilot.eclipse.ui/icons/spinner/6@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..5f38df02e988bc35452e0791675f5c2e5166e729
GIT binary patch
literal 1763
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
literal 0
HcmV?d00001
diff --git a/com.microsoft.copilot.eclipse.ui/plugin.properties b/com.microsoft.copilot.eclipse.ui/plugin.properties
index 4aadecf3..0b65d477 100644
--- a/com.microsoft.copilot.eclipse.ui/plugin.properties
+++ b/com.microsoft.copilot.eclipse.ui/plugin.properties
@@ -3,4 +3,5 @@ command.acceptFullSuggestion.name=Accept Suggestion
command.discardSuggestion.name=Discard Suggestion
command.copilotForEclipsePlugin.name=GitHub Copilot for Eclipse
command.signInToGitHub.name=Sign in to GitHub Copilot
-command.signOutFromGitHub.name=Sign out from GitHub Copilot
\ No newline at end of file
+command.signOutFromGitHub.name=Sign out from GitHub Copilot
+command.viewFeedbackForum.name=View Feedback Forum
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/plugin.xml b/com.microsoft.copilot.eclipse.ui/plugin.xml
index 466def52..f2786167 100644
--- a/com.microsoft.copilot.eclipse.ui/plugin.xml
+++ b/com.microsoft.copilot.eclipse.ui/plugin.xml
@@ -51,6 +51,10 @@
id="com.microsoft.copilot.eclipse.commands.discardSuggestion"
name="%command.discardSuggestion.name">
+
+
@@ -80,6 +84,10 @@
class="com.microsoft.copilot.eclipse.ui.handlers.DiscardSuggestionHandler"
commandId="com.microsoft.copilot.eclipse.commands.discardSuggestion">
+
+
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
index b5dc6d23..8fffbdd6 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
@@ -7,12 +7,10 @@
import org.osgi.framework.BundleContext;
import com.microsoft.copilot.eclipse.core.CopilotCore;
-import com.microsoft.copilot.eclipse.core.completion.CompletionListener;
-import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
import com.microsoft.copilot.eclipse.core.logger.CopilotForEclipseLogger;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
-import com.microsoft.copilot.eclipse.ui.completion.CompletionStatusListener;
+import com.microsoft.copilot.eclipse.ui.completion.CompletionStatusManager;
import com.microsoft.copilot.eclipse.ui.completion.EditorLifecycleListener;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
@@ -25,7 +23,7 @@ public class CopilotUi extends Plugin {
private static final int RETRY_COUNT = 30;
private static CopilotUi COPILOT_UI_PLUGIN = null;
- private CompletionStatusListener completionStatusListener;
+ private CompletionStatusManager completionStatusManager;
private EditorLifecycleListener editorLifecycleListener;
private EditorsManager editorsManager;
public static final CopilotForEclipseLogger LOGGER = new CopilotForEclipseLogger(CopilotCore.class.getName());
@@ -63,19 +61,24 @@ public void start(BundleContext context) throws Exception {
this.editorsManager = new EditorsManager(connection, CopilotCore.getPlugin().getCompletionProvider());
this.editorLifecycleListener = new EditorLifecycleListener(editorsManager);
- this.completionStatusListener = new CompletionStatusListener();
+ this.completionStatusManager = new CompletionStatusManager();
registerPartListener();
- registerCompletionListener();
+ addCompletionStatusListener();
// Initialize the completion handler for the active editor in case we miss the event
// to initialize it.
initCompletionHandlerForActiveEditor();
}
+
+ public CompletionStatusManager getCompletionStatusManager() {
+ return completionStatusManager;
+ }
@Override
public void stop(BundleContext context) throws Exception {
unregisterPartListener();
+ removeCompletionStatusListener();
if (this.editorsManager != null) {
this.editorsManager.dispose();
}
@@ -92,8 +95,8 @@ private void registerPartListener() {
}
}
- private void registerCompletionListener() {
- CopilotCore.getPlugin().getCompletionProvider().addCompletionListener(this.completionStatusListener);
+ private void addCompletionStatusListener() {
+ CopilotCore.getPlugin().getCompletionProvider().addCompletionStatusListener(this.completionStatusManager);
}
private void initCompletionHandlerForActiveEditor() {
@@ -110,8 +113,8 @@ private void unregisterPartListener() {
}
}
- private void unregisterCompletionListener() {
- CopilotCore.getPlugin().getCompletionProvider().removeCompletionListener(this.completionStatusListener);
+ private void removeCompletionStatusListener() {
+ CopilotCore.getPlugin().getCompletionProvider().removeCompletionStatusListener(this.completionStatusManager);
}
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/UiConstants.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/UiConstants.java
index f6df7406..da347bad 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/UiConstants.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/UiConstants.java
@@ -17,4 +17,9 @@ private UiConstants() {
*/
public static final int DEFAULT_GHOST_TEXT_SCALE = 112;
+
+ /**
+ * The URL constants for the Copilot menu.
+ */
+ public static final String COPILOT_FEEDBACK_FORUM_URL = "https://github.com/orgs/community/discussions/categories/copilot";
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusListener.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusListener.java
deleted file mode 100644
index 01a6e637..00000000
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusListener.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.microsoft.copilot.eclipse.ui.completion;
-
-import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
-import com.microsoft.copilot.eclipse.core.completion.CompletionListener;
-import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
-
-/**
- * Listener for tracking copilot completion status.
- */
-public class CompletionStatusListener implements CompletionListener {
-
- @Override
- public void onCompletionResolved(CompletionCollection completions) {
- // do nothing
- }
-}
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusManager.java
new file mode 100644
index 00000000..30ef37c1
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusManager.java
@@ -0,0 +1,34 @@
+package com.microsoft.copilot.eclipse.ui.completion;
+
+import com.microsoft.copilot.eclipse.core.completion.CompletionStatusListener;
+import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
+
+/**
+ * Listener for tracking copilot completion status.
+ */
+public class CompletionStatusManager implements CompletionStatusListener {
+
+ private boolean completionInProgress;
+
+ /**
+ * Constructor for the CompletionStatusManager.
+ */
+ public CompletionStatusManager() {
+ }
+
+ @Override
+ public void onCompletionAboutToRun() {
+ this.completionInProgress = true;
+ UiUtils.refreshCopilotMenu();
+ }
+
+ @Override
+ public void onCompletionDone() {
+ this.completionInProgress = false;
+ UiUtils.refreshCopilotMenu();
+ }
+
+ public boolean isCompletionInProgress() {
+ return completionInProgress;
+ }
+}
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java
index 61070cc2..b3692fea 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java
@@ -7,6 +7,7 @@
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
+import com.microsoft.copilot.eclipse.ui.completion.CompletionStatusManager;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
/**
@@ -32,4 +33,8 @@ public CompletionHandler getActiveCompletionHandler() {
public CopilotLanguageServerConnection getLanguageServerConnection() {
return CopilotCore.getPlugin().getCopilotLanguageServer();
}
+
+ public CompletionStatusManager getCompletionStatusManager() {
+ return CopilotUi.getPlugin().getCompletionStatusManager();
+ }
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
index 7753b768..5ce7a299 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
@@ -1,10 +1,14 @@
package com.microsoft.copilot.eclipse.ui.handlers;
+import java.util.Map;
import java.util.Objects;
-import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
@@ -12,24 +16,27 @@
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.IElementUpdater;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.handlers.IHandlerService;
+import org.eclipse.ui.menus.UIElement;
import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
+import com.microsoft.copilot.eclipse.ui.completion.CompletionStatusManager;
import com.microsoft.copilot.eclipse.ui.i18n.Messages;
import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
/**
* Handler for showing GitHub Copilot status bar menu.
*/
-public class ShowStatusBarMenuHandler extends AbstractHandler {
-
+public class ShowStatusBarMenuHandler extends CopilotHandler implements IElementUpdater {
private IHandlerService handlerService;
private CopilotStatusManager copilotStatusManager;
+ private SpinnerJob spinnerJob;
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
@@ -37,10 +44,17 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
copilotStatusManager = CopilotCore.getPlugin().getCopilotStatusManager();
MenuManager menuManager = new MenuManager();
+ // Sign in status section
addStatusAction(menuManager);
+ // References to the usage of GitHub Copilot section
+ // TODO: Uncomment to enable the feedback forum link
+ // menuManager.add(new Separator());
+ // addLinkToFeedbackForumAction(menuManager);
+
+ // Sign in & sign out section
+ menuManager.add(new Separator());
if (!Objects.equals(copilotStatusManager.getCopilotStatus(), CopilotStatusResult.LOADING)) {
- menuManager.add(new Separator());
addSignInOrSignOutAction(menuManager);
}
@@ -50,32 +64,88 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
return null;
}
+ @Override
+ public void updateElement(UIElement element, Map parameters) {
+ CompletionStatusManager completionStatusManager = getCompletionStatusManager();
+
+ if (completionStatusManager.isCompletionInProgress()) {
+ scheduleSpinnerJob(element);
+ } else {
+ // Since spinner job has 200ms delay, cancel the spinner job if it is running to avoid flickering.
+ if (spinnerJob != null) {
+ spinnerJob.cancel();
+ }
+
+ String copilotStatus = CopilotCore.getPlugin().getCopilotStatusManager().getCopilotStatus();
+ String iconPath = null;
+
+ switch (copilotStatus) {
+ case CopilotStatusResult.OK:
+ iconPath = "/icons/github_copilot_signed_in_blue.png";
+ break;
+ case CopilotStatusResult.LOADING:
+ scheduleSpinnerJob(element);
+ break;
+ case CopilotStatusResult.ERROR:
+ case CopilotStatusResult.WARNING:
+ iconPath = "/icons/github_copilot_error_blue.png";
+ break;
+ case CopilotStatusResult.NOT_SIGNED_IN:
+ case CopilotStatusResult.NOT_AUTHORIZED:
+ default:
+ iconPath = "/icons/github_copilot_not_signed_in_blue.png";
+ }
+
+ if (iconPath != null) {
+ ImageDescriptor newIcon = UiUtils.buildImageDescriptorFromPngPath(iconPath);
+ element.setIcon(newIcon);
+ }
+ }
+ }
+
private void addStatusAction(MenuManager menuManager) {
- String signInStatus = getSignInStatusBasedOnAuthResult(copilotStatusManager.getCopilotStatus());
- String signInStatusTitle = Messages.menu_signInStatus + ": " + signInStatus;
+ String copilotStatus = getCopilotStatusBasedOnAuthAndCompletionResult(copilotStatusManager.getCopilotStatus());
+ String copilotStatusTitle = Messages.menu_copilotStatus + ": " + copilotStatus;
- MenuActionFactory.createMenuAction(menuManager, signInStatusTitle, handlerService, signInStatus, false);
+ MenuActionFactory.createMenuAction(menuManager, copilotStatusTitle, handlerService, copilotStatus, false);
}
- private String getSignInStatusBasedOnAuthResult(String copilotStatus) {
+ private void addLinkToFeedbackForumAction(MenuManager menuManager) {
+ MenuActionFactory.createMenuAction(menuManager, Messages.menu_viewFeedbackForum, handlerService,
+ "com.microsoft.copilot.eclipse.commands.viewFeedbackForum", true);
+ }
+
+ private String getCopilotStatusBasedOnAuthAndCompletionResult(String copilotStatus) {
+ CompletionStatusManager completionStatusManager = getCompletionStatusManager();
switch (copilotStatus) {
case CopilotStatusResult.OK:
- return Messages.menu_signInStatus_ready;
+ return completionStatusManager.isCompletionInProgress() ? Messages.menu_copilotStatus_completionInProgress
+ : Messages.menu_copilotStatus_ready;
case CopilotStatusResult.ERROR:
- return Messages.menu_signInStatus_unknownError;
+ return Messages.menu_copilotStatus_unknownError;
case CopilotStatusResult.LOADING:
- return Messages.menu_signInStatus_loading;
+ return Messages.menu_copilotStatus_loading;
case CopilotStatusResult.NOT_SIGNED_IN:
- return Messages.menu_signInStatus_notSignedInToGitHub;
+ return Messages.menu_copilotStatus_notSignedInToGitHub;
case CopilotStatusResult.WARNING:
- return Messages.menu_signInStatus_agentWarning;
+ return Messages.menu_copilotStatus_agentWarning;
case CopilotStatusResult.NOT_AUTHORIZED:
- return Messages.menu_signInStatus_notAuthorized;
+ return Messages.menu_copilotStatus_notAuthorized;
default:
- return Messages.menu_signInStatus_loading;
+ return Messages.menu_copilotStatus_loading;
}
}
+ private void scheduleSpinnerJob(UIElement uiElement) {
+ if (spinnerJob != null) {
+ spinnerJob.cancel();
+ } else {
+ spinnerJob = new SpinnerJob();
+ }
+ spinnerJob.setTargetUiElement(uiElement);
+ spinnerJob.schedule();
+ }
+
private void addSignInOrSignOutAction(MenuManager menuManager) {
if (Objects.equals(copilotStatusManager.getCopilotStatus(), CopilotStatusResult.OK)) {
ImageDescriptor signInIcon = UiUtils.buildImageDescriptorFromPngPath("/icons/signin.png");
@@ -110,4 +180,45 @@ public static void createMenuAction(MenuManager menuManager, String text, IHandl
createMenuAction(menuManager, text, null, handlerService, commandId, enabled);
}
}
+
+ private class SpinnerJob extends Job {
+ private static final int INITIAL_ICON_INDEX = 1;
+ private static final int TOTAL_SPINNER_ICONS = 8;
+ private static final long COMPLETION_IN_PROGRESS_SPINNER_ROTATE_RATE_MILLIS = 200L;
+
+
+ private int currentIconIndex = INITIAL_ICON_INDEX;
+ private UIElement uiElement;
+
+ public SpinnerJob() {
+ super("Spinner Job");
+ this.setSystem(true);
+ }
+
+ public void setTargetUiElement(UIElement uiElement) {
+ this.uiElement = uiElement;
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ if (this.uiElement == null) {
+ throw new IllegalStateException("UI element is not set. Spinner cannot be set.");
+ }
+ String iconPath = String.format("/icons/spinner/%d.png", currentIconIndex);
+ ImageDescriptor newIcon = UiUtils.buildImageDescriptorFromPngPath(iconPath);
+ this.uiElement.setIcon(newIcon);
+ currentIconIndex = (currentIconIndex % TOTAL_SPINNER_ICONS) + 1;
+ if (CopilotUi.getPlugin().getCompletionStatusManager().isCompletionInProgress()) {
+ schedule(COMPLETION_IN_PROGRESS_SPINNER_ROTATE_RATE_MILLIS);
+ } else {
+ cancel();
+ }
+ } catch (Exception e) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
+ return Status.CANCEL_STATUS;
+ }
+ return Status.OK_STATUS;
+ }
+ }
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ViewFeedbackForumHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ViewFeedbackForumHandler.java
new file mode 100644
index 00000000..9831fc82
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ViewFeedbackForumHandler.java
@@ -0,0 +1,28 @@
+package com.microsoft.copilot.eclipse.ui.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
+import com.microsoft.copilot.eclipse.ui.CopilotUi;
+import com.microsoft.copilot.eclipse.ui.UiConstants;
+import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
+
+/**
+ * Open the Copilot feedback forum in browser.
+ */
+public class ViewFeedbackForumHandler extends AbstractHandler {
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ try {
+ UiUtils.openLink(UiConstants.COPILOT_FEEDBACK_FORUM_URL);
+ } catch (Exception e) {
+ CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ }
+
+ return null;
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
index fcf7264b..02f42a04 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
@@ -7,15 +7,17 @@
*/
public final class Messages extends NLS {
private static final String BUNDLE_NAME = "com.microsoft.copilot.eclipse.ui.i18n.messages"; //$NON-NLS-1$
- public static String menu_signInStatus;
- public static String menu_signInStatus_ready;
- public static String menu_signInStatus_loading;
- public static String menu_signInStatus_notSignedInToGitHub;
- public static String menu_signInStatus_unknownError;
- public static String menu_signInStatus_notAuthorized;
- public static String menu_signInStatus_agentWarning;
+ public static String menu_copilotStatus;
+ public static String menu_copilotStatus_ready;
+ public static String menu_copilotStatus_loading;
+ public static String menu_copilotStatus_completionInProgress;
+ public static String menu_copilotStatus_notSignedInToGitHub;
+ public static String menu_copilotStatus_unknownError;
+ public static String menu_copilotStatus_notAuthorized;
+ public static String menu_copilotStatus_agentWarning;
public static String menu_signToGitHub;
public static String menu_signOutFromGitHub;
+ public static String menu_viewFeedbackForum;
public static String signInDialog_title;
public static String signInDialog_button_cancel;
public static String signInDialog_button_copyOpen;
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
index 5a1d1015..52e9adb9 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
@@ -1,12 +1,14 @@
-menu_signInStatus=Status
-menu_signInStatus_ready=Ready
-menu_signInStatus_loading=Loading
-menu_signInStatus_notSignedInToGitHub=Not signed in to GitHub
-menu_signInStatus_unknownError=Unknown error
-menu_signInStatus_notAuthorized=No access to GitHub Copilot
-menu_signInStatus_agentWarning=Copilot is encountering temporary issues
+menu_copilotStatus=Status
+menu_copilotStatus_ready=Ready
+menu_copilotStatus_loading=Loading
+menu_copilotStatus_completionInProgress=Copilot completing in progress
+menu_copilotStatus_notSignedInToGitHub=Not signed in to GitHub
+menu_copilotStatus_unknownError=Unknown error
+menu_copilotStatus_notAuthorized=No access to GitHub Copilot
+menu_copilotStatus_agentWarning=Copilot is encountering temporary issues
menu_signToGitHub=Sign In to GitHub
menu_signOutFromGitHub=Sign Out from GitHub
+menu_viewFeedbackForum=View Feedback Forum...
signInDialog_title=Sign In to GitHub
signInDialog_button_cancel=Cancel
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
index 37b7d18d..87448fa7 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
@@ -16,6 +16,8 @@
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.browser.IWebBrowser;
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.menus.UIElement;
import org.eclipse.ui.texteditor.ITextEditor;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
@@ -95,7 +97,17 @@ public static int widgetOffset2ModelOffset(ITextViewer textViewer, int offset) {
/**
* Builds an image descriptor from a PNG file at the given path.
*/
- public static final ImageDescriptor buildImageDescriptorFromPngPath(String path) {
+ public static ImageDescriptor buildImageDescriptorFromPngPath(String path) {
return ImageDescriptor.createFromURL(UiUtils.class.getResource(path));
}
+
+ /**
+ * Refreshes the elements of the command with the given ID.
+ */
+ public static void refreshCopilotMenu() {
+ ICommandService commandService = PlatformUI.getWorkbench().getService(ICommandService.class);
+ if (commandService != null) {
+ commandService.refreshElements("com.microsoft.copilot.eclipse.commands.showStatusBarMenu", null);
+ }
+ }
}
From 9f78a31d980c03a3fb36c06f5c626047d8dc83e8 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Tue, 24 Dec 2024 16:59:27 +0800
Subject: [PATCH 037/690] fix - take the completion range into consideration
when accept suggestion (#55)
---
.../core/completion/CompletionCollection.java | 22 ++++
.../META-INF/MANIFEST.MF | 4 +-
.../ui/completion/CompletionManagerTests.java | 112 ++++++++++++++++++
.../ui/completion/CompletionManager.java | 10 +-
4 files changed, 142 insertions(+), 6 deletions(-)
create mode 100644 com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManagerTests.java
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollection.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollection.java
index bc1ad2f6..bb5a87c4 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollection.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionCollection.java
@@ -3,6 +3,8 @@
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.Range;
import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
@@ -96,6 +98,26 @@ public int getNumberOfLines() {
return this.getText().split("\n").length;
}
+ /**
+ * Get the position where the completion was triggered.
+ */
+ public Position getTriggerPosition() {
+ if (this.completions.isEmpty()) {
+ throw new IllegalStateException("completions cannot be empty");
+ }
+ return this.completions.get(index).getPosition();
+ }
+
+ /**
+ * Get the range for the completion.
+ */
+ public Range getRange() {
+ if (this.completions.isEmpty()) {
+ throw new IllegalStateException("completions cannot be empty");
+ }
+ return this.completions.get(index).getRange();
+ }
+
public List getUuids() {
return this.completions.stream().map(CompletionItem::getUuid).toList();
}
diff --git a/com.microsoft.copilot.eclipse.ui.test/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.ui.test/META-INF/MANIFEST.MF
index b17ef99d..a31cae6f 100644
--- a/com.microsoft.copilot.eclipse.ui.test/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.ui.test/META-INF/MANIFEST.MF
@@ -19,4 +19,6 @@ Require-Bundle: org.mockito.mockito-core;bundle-version="5.14.2",
org.eclipse.ui.ide,
org.eclipse.ui.workbench.texteditor,
org.eclipse.jface.text;bundle-version="3.25.200",
- org.eclipse.lsp4j;bundle-version="0.23.1"
+ org.eclipse.lsp4j;bundle-version="0.23.1",
+ org.eclipse.core.resources,
+ org.eclipse.lsp4j.jsonrpc;bundle-version="0.23.1"
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManagerTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManagerTests.java
new file mode 100644
index 00000000..e40314ee
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManagerTests.java
@@ -0,0 +1,112 @@
+package com.microsoft.copilot.eclipse.ui.completion;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextOperationTarget;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.Range;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
+import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
+import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
+
+@ExtendWith(MockitoExtension.class)
+class CompletionManagerTests {
+
+ private IProject project;
+
+ @Mock
+ private CopilotLanguageServerConnection mockLsConnection;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ project = ResourcesPlugin.getWorkspace().getRoot().getProject("TestProject");
+ project.create(null);
+ project.open(null);
+ }
+
+ @AfterEach
+ public void tearDown() throws Exception {
+ project.delete(true, null);
+ }
+
+ @Test
+ void testReplaceCompletion_1() throws Exception {
+ IFile file = project.getFile("Test.java");
+ String content = """
+ public class App {
+
+ public void hi() {
+ System.out.println("");
+ }
+
+ }
+ """;
+ file.create(content.getBytes(), IResource.FORCE, null);
+ int documentVersion = 1;
+
+ IEditorPart editorPart = getEditorPartFor(file);
+ assertTrue(editorPart instanceof ITextEditor);
+
+ ITextEditor textEditor = (ITextEditor) editorPart;
+ ITextViewer textViewer = (ITextViewer) textEditor.getAdapter(ITextOperationTarget.class);
+ IDocument document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
+
+ when(mockLsConnection.getDocumentVersion(any())).thenReturn(documentVersion);
+ CompletionManager manager = new CompletionManager(mockLsConnection, mock(CompletionProvider.class), textViewer,
+ document, file.getLocationURI());
+
+ List completions = List.of(new CompletionItem("uuid", " System.out.println(\"hi\");",
+ new Range(new Position(3, 0), new Position(3, 27)), "hi\");", new Position(3, 24), documentVersion));
+ CompletionCollection completionsCollection = new CompletionCollection(completions,
+ file.getLocationURI().toASCIIString());
+
+ manager.onCompletionResolved(completionsCollection);
+ manager.acceptSuggestion();
+
+ assertTrue(document.get().contains(" System.out.println(\"hi\");\n"));
+ }
+
+ protected IEditorPart getEditorPartFor(IFile file) {
+ AtomicReference ref = new AtomicReference<>();
+ SwtUtils.invokeOnDisplayThread(() -> {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ if (window != null) {
+ try {
+ ref.set(window.getActivePage().openEditor(new org.eclipse.ui.part.FileEditorInput(file),
+ "org.eclipse.ui.DefaultTextEditor"));
+ } catch (PartInitException e) {
+ }
+ }
+ });
+ return ref.get();
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
index a2bf110b..41f5ec6b 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
@@ -57,7 +57,7 @@ public CompletionManager(CopilotLanguageServerConnection lsConnection, Completio
this.documentUri = documentUri;
this.completions = null;
- this.triggerPosition = new org.eclipse.jface.text.Position(0);
+ this.triggerPosition = new Position(0);
this.textViewer = textViewer;
StyledText styledText = textViewer.getTextWidget();
if (styledText != null) {
@@ -179,16 +179,16 @@ public boolean hasCompletion() {
* @throws BadLocationException if the offset is invalid.
*/
public void acceptSuggestion() throws BadLocationException {
- int offset = this.triggerPosition.offset;
- if (offset < 0) {
+ if (this.completions == null || this.completions.getSize() == 0) {
return;
}
-
+ int startOffset = LSPEclipseUtils.toOffset(this.completions.getTriggerPosition(), this.document);
String text = this.completions.getText();
if (StringUtils.isEmpty(text)) {
return;
}
- this.document.replace(offset, 0, text);
+ int endOffset = LSPEclipseUtils.toOffset(this.completions.getRange().getEnd(), this.document);
+ this.document.replace(startOffset, endOffset - startOffset, text);
}
public CompletionCollection getCompletions() {
From f06e13d4d775a1d7aacbf0c40cc20539d2c7a6b7 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Tue, 24 Dec 2024 17:00:23 +0800
Subject: [PATCH 038/690] fix - Add timeout for completion job (#48)
---
.../core}/completion/CompletionJobTests.java | 34 +++++++++++++++++--
.../core/completion/CompletionJob.java | 13 ++++++-
2 files changed, 44 insertions(+), 3 deletions(-)
rename {com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui => com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core}/completion/CompletionJobTests.java (65%)
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/CompletionJobTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionJobTests.java
similarity index 65%
rename from com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/CompletionJobTests.java
rename to com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionJobTests.java
index d1fa4362..5c5cb4ee 100644
--- a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/CompletionJobTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionJobTests.java
@@ -1,19 +1,25 @@
-package com.microsoft.copilot.eclipse.ui.completion;
+package com.microsoft.copilot.eclipse.core.completion;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobManager;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.lsp4j.Position;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
-import com.microsoft.copilot.eclipse.core.completion.CompletionJob;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionDocument;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
@@ -70,4 +76,28 @@ void testTriggerCompletionJobWithParams() throws InterruptedException {
assertEquals(IStatus.OK, status.getSeverity());
assertEquals(expectedResult, completionJob.getCompletionResult());
}
+
+ @Test
+ void testShouldTimeoutWhenCompletionTakesTooLong() throws Exception {
+ when(mockLsConnection.getCompletions(any())).thenAnswer(invocation -> {
+ TimeUnit.SECONDS.sleep(6); // completion will timeout after 5 seconds
+ return new CompletableFuture<>();
+ });
+
+ CompletionJob job = new CompletionJob(mockLsConnection);
+ Position position = new Position(0, 0);
+ CompletionDocument completionDoc = new CompletionDocument("file://test.java", position);
+ completionDoc.setVersion(1);
+ completionDoc.setInsertSpaces(true);
+ completionDoc.setTabSize(4);
+ job.setCompletionParams(new CompletionParams(completionDoc));
+ job.schedule();
+
+ IJobManager jobManager = Job.getJobManager();
+ jobManager.join(CompletionJob.COMPLETION_JOB_FAMILY, new NullProgressMonitor());
+
+ assertEquals(Status.CANCEL_STATUS, job.getResult());
+
+ }
+
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java
index c5933268..32f9fbf0 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java
@@ -2,6 +2,8 @@
import java.util.Objects;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
@@ -20,8 +22,13 @@
*/
public class CompletionJob extends Job {
+ /**
+ * The job family for completion jobs, can be used to find out this completion job.
+ */
public static final String COMPLETION_JOB_FAMILY = "com.microsoft.copilot.eclipse.completionJobFamily";
+ private static final int COMPLETION_TIMEOUT = 5000;
+
private CompletionResult result;
private CopilotLanguageServerConnection lsConnection;
@@ -54,12 +61,16 @@ protected IStatus run(IProgressMonitor monitor) {
return Status.CANCEL_STATUS;
}
try {
- this.result = this.lsConnection.getCompletions(params).get();
+ this.result = this.lsConnection.getCompletions(params).get(COMPLETION_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
} catch (ExecutionException e) {
CopilotCore.LOGGER.log(LogLevel.ERROR, e);
return new Status(IStatus.ERROR, Constants.PLUGIN_ID, e.getMessage(), e);
+ } catch (TimeoutException e) {
+ CopilotCore.LOGGER.log(LogLevel.WARNING,
+ "Completion request timed out after " + COMPLETION_TIMEOUT + " milliseconds");
+ return Status.CANCEL_STATUS;
}
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
From 69ff97d4ec666c9379622aab605bfc6884f9f125 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Tue, 24 Dec 2024 18:40:43 +0800
Subject: [PATCH 039/690] fix - Reset the line vertical indentation correctly
(#63)
---
.../core/completion/CompletionJob.java | 6 +++---
.../ui/completion/CompletionHandler.java | 2 +-
.../ui/completion/CompletionManager.java | 21 ++++++++++++++++---
3 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java
index 32f9fbf0..995b0e4f 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java
@@ -27,7 +27,7 @@ public class CompletionJob extends Job {
*/
public static final String COMPLETION_JOB_FAMILY = "com.microsoft.copilot.eclipse.completionJobFamily";
- private static final int COMPLETION_TIMEOUT = 5000;
+ private static final int COMPLETION_TIMEOUT_MILLIS = 5000;
private CompletionResult result;
@@ -61,7 +61,7 @@ protected IStatus run(IProgressMonitor monitor) {
return Status.CANCEL_STATUS;
}
try {
- this.result = this.lsConnection.getCompletions(params).get(COMPLETION_TIMEOUT, TimeUnit.MILLISECONDS);
+ this.result = this.lsConnection.getCompletions(params).get(COMPLETION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
} catch (ExecutionException e) {
@@ -69,7 +69,7 @@ protected IStatus run(IProgressMonitor monitor) {
return new Status(IStatus.ERROR, Constants.PLUGIN_ID, e.getMessage(), e);
} catch (TimeoutException e) {
CopilotCore.LOGGER.log(LogLevel.WARNING,
- "Completion request timed out after " + COMPLETION_TIMEOUT + " milliseconds");
+ "Completion request timed out after " + COMPLETION_TIMEOUT_MILLIS + " milliseconds");
return Status.CANCEL_STATUS;
}
if (monitor.isCanceled()) {
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
index b5962e63..75749bef 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
@@ -127,7 +127,7 @@ public void triggerCompletion() {
* Clear the completion ghost text.
*/
public void clearCompletionRendering() {
- this.completionManager.clearGhostText(this.triggerPosition);
+ this.completionManager.clearGhostText();
}
public CompletionCollection getCompletions() {
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
index 41f5ec6b..5c76d34d 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
@@ -85,11 +85,26 @@ public void triggerCompletion(Position position, int documentVersion) {
/**
* Clear the completion.
*/
- public void clearGhostText(Position position) {
- this.triggerPosition = position;
+ public void clearGhostText() {
+ if (this.completions == null || this.completions.getSize() == 0) {
+ return;
+ }
+ try {
+ // use completion trigger position if available. this.triggerPosition is the current
+ // cursor position, which may not be the same as the completion trigger position when user
+ // use mouse to move the cursor. In that case, the line vertical indentation might not be
+ // reset correctly.
+ int offset = LSPEclipseUtils.toOffset(this.completions.getTriggerPosition(), this.document);
+ this.triggerPosition = new Position(offset);
+ } catch (BadLocationException e) {
+ CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ return;
+ }
this.completions = null;
StyledText styledText = textViewer.getTextWidget();
if (styledText != null) {
+ this.setLineVerticalIndentation(styledText, null,
+ UiUtils.modelOffset2WidgetOffset(textViewer, this.triggerPosition.getOffset()));
SwtUtils.invokeOnDisplayThread(styledText::redraw, styledText);
}
@@ -153,7 +168,7 @@ public void paintControl(PaintEvent e) {
private void setLineVerticalIndentation(StyledText styledText, GC gc, int widgetOffset) {
int height = 0;
- if (this.completions != null) {
+ if (this.completions != null && gc != null) {
// Change the height (line vertical indentation) to fit the line of
// ghost text.
Point ghostTextExtent = gc.textExtent(this.completions.getText());
From 6a967437af0e11e0b22a4e18fa35beb558fd51e0 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Wed, 25 Dec 2024 13:51:05 +0800
Subject: [PATCH 040/690] build - Add code sign (#67)
---
.azure-pipelines/nightly.yml | 59 ++++++++++++++++++++++++++++--------
1 file changed, 46 insertions(+), 13 deletions(-)
diff --git a/.azure-pipelines/nightly.yml b/.azure-pipelines/nightly.yml
index 10e2f133..bbd65cd1 100644
--- a/.azure-pipelines/nightly.yml
+++ b/.azure-pipelines/nightly.yml
@@ -38,6 +38,17 @@ extends:
- checkout: self
fetchTags: false
+ - task: UsePythonVersion@0
+ displayName: 'Use Python 3.11.x'
+ inputs:
+ versionSpec: 3.11.x
+
+ - task: UseDotNet@2
+ displayName: 'Use .NET Core 3.1.x'
+ inputs:
+ packageType: 'sdk'
+ version: '3.1.x'
+
- task: JavaToolInstaller@0
displayName: Use Java 17
inputs:
@@ -50,6 +61,15 @@ extends:
inputs:
version: '20.x'
+ - task: MicroBuildSigningPlugin@4
+ displayName: 'Install Signing Plugin'
+ inputs:
+ signType: real
+ azureSubscription: 'MicroBuild Signing Task (MSEng)'
+ feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json'
+ env:
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+
- bash: npm i
workingDirectory: com.microsoft.copilot.eclipse.core/copilot-agent
displayName: Install Copilot LS
@@ -57,24 +77,37 @@ extends:
- bash: ./mvnw clean package
displayName: 'Run Maven Clean and Package'
- # TODO: support code sign
- bash: |
mkdir -p ./artifacts/eclipse/
cp ./com.microsoft.copilot.eclipse.repository/target/com.microsoft.copilot.eclipse.repository*.zip ./artifacts/eclipse/GithubCopilotForEclipse.zip
- # unzip ./artifacts/eclipse/GithubCopilotForEclipse.zip "**/*.jar" "*.jar" -d ./artifacts/eclipse/folder
- # rm ./artifacts/eclipse/GithubCopilotForEclipse.zip
+ unzip ./artifacts/eclipse/GithubCopilotForEclipse.zip "**/*.jar" "*.jar" -d ./artifacts/eclipse/folder
+ rm ./artifacts/eclipse/GithubCopilotForEclipse.zip
+
+ ## Workaround: Remove MD5/SHA256 Validation in artifacts.xml
+ cd ./artifacts/eclipse/folder
+ unzip artifacts.jar -d ./artifacts
+ rm artifacts.jar
+ cd artifacts
+ sed -i -E '/download\.md5|checksum/d' ./artifacts.xml
+ zip -R ./artifacts.jar * **/*
+ mv ./artifacts.jar ../artifacts.jar
+ cd ..
+ rm -rf ./artifacts
+ displayName: Prepare plugin zip
+
+ - task: CmdLine@2
+ displayName: Sign jars
+ inputs:
+ script: |
+ files=$(find . -type f -name "*.jar")
+ for file in $files; do
+ dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$file" /certs:100010171
+ done
+ workingDirectory: 'artifacts/eclipse/folder'
- # ## Workaround: Remove MD5/SHA256 Validation in artifacts.xml
- # cd ./artifacts/eclipse/folder
- # unzip artifacts.jar -d ./artifacts
- # rm artifacts.jar
- # cd artifacts
- # sed -i -E '/download\.md5|checksum/d' ./artifacts.xml
- # zip -R ./artifacts.jar * **/*
- # mv ./artifacts.jar ../artifacts.jar
- # cd ..
- # rm -rf ./artifacts
+ - bash: cd ./artifacts/eclipse/folder && zip -R ../GithubCopilotForEclipse.zip **/*.jar *.jar
+ displayName: Package zip
- task: CopyFiles@2
displayName: Copy plugin zip
From e35398da456e2f0c4c346b419e8d347569a8e03d Mon Sep 17 00:00:00 2001
From: ethanyhou <149548697+ethanyhou@users.noreply.github.com>
Date: Wed, 25 Dec 2024 14:01:07 +0800
Subject: [PATCH 041/690] fix - Menu icon stuck in the error status. (#66)
---
.../copilot/eclipse/core/CopilotStatusManager.java | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
index ca0e6049..7f379ea8 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
@@ -1,8 +1,6 @@
package com.microsoft.copilot.eclipse.core;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
@@ -18,8 +16,6 @@ public class CopilotStatusManager {
private CopilotStatusResult copilotStatusResult;
- private static final int CHECK_STATUS_TIMEOUT_MILLIS = 3000;
-
/**
* Constructor for the CopilotStatusManager.
*
@@ -86,9 +82,7 @@ public CopilotStatusResult setCompletionDone() {
* Check the login status for current machine.
*/
public void checkStatus() {
- CompletableFuture statusFuture = this.connection.checkStatus(false);
-
- statusFuture.orTimeout(CHECK_STATUS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS).thenAccept(result -> {
+ this.connection.checkStatus(false).thenAccept(result -> {
this.copilotStatusResult = result;
}).exceptionally(ex -> {
CopilotCore.LOGGER.log(LogLevel.ERROR, ex);
From 79e250e6a5373cadcb251f9aa11485682f782673 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Wed, 25 Dec 2024 19:03:55 +0800
Subject: [PATCH 042/690] fix - UI freeze on startup (#68)
* This change wraps the UI activation in a Job to prevent the UI
from freezing on startup.
---
.../copilot/eclipse/core/CopilotCore.java | 14 +++-
.../copilot/eclipse/ui/CopilotUiTests.java | 5 ++
.../copilot/eclipse/ui/CopilotUi.java | 66 +++++++++++--------
3 files changed, 57 insertions(+), 28 deletions(-)
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
index bda5ecf2..aeb37af4 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
@@ -1,5 +1,7 @@
package com.microsoft.copilot.eclipse.core;
+import java.util.Objects;
+
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Plugin;
@@ -28,6 +30,11 @@ public class CopilotCore extends Plugin {
private static CopilotCore COPILOT_CORE_PLUGIN = null;
public static final CopilotForEclipseLogger LOGGER = new CopilotForEclipseLogger(CopilotCore.class.getName());
+ /**
+ * The job family for the initialization job.
+ */
+ public static final String INIT_JOB_FAMILY = "com.microsoft.copilot.eclipse.core.initJob";
+
/**
* Creates the Copilot core plugin. The plugin is created automatically by the Eclipse framework. Clients must not
* call this constructor.
@@ -78,8 +85,13 @@ protected IStatus run(IProgressMonitor monitor) {
initRunnable.run();
return Status.OK_STATUS;
}
+
+ @Override
+ public boolean belongsTo(Object family) {
+ return Objects.equals(INIT_JOB_FAMILY, family);
+ }
};
- initJob.setUser(true);
+ initJob.setUser(false);
initJob.schedule();
}
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/CopilotUiTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/CopilotUiTests.java
index 14409d29..fc1aba10 100644
--- a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/CopilotUiTests.java
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/CopilotUiTests.java
@@ -4,9 +4,12 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.jobs.Job;
import org.junit.jupiter.api.Test;
import org.osgi.framework.Bundle;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+
class CopilotUiTests {
@Test
@@ -14,6 +17,8 @@ void testCopilotCoreWakeUp() throws Exception {
CopilotUi ui = new CopilotUi();
ui.start(null);
+ Job.getJobManager().join(CopilotCore.INIT_JOB_FAMILY, null);
+
Bundle bundle = Platform.getBundle("com.microsoft.copilot.eclipse.core");
assertNotNull(bundle);
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
index 8fffbdd6..4db36271 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
@@ -1,6 +1,11 @@
package com.microsoft.copilot.eclipse.ui;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Plugin;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
@@ -20,7 +25,6 @@
*/
public class CopilotUi extends Plugin {
- private static final int RETRY_COUNT = 30;
private static CopilotUi COPILOT_UI_PLUGIN = null;
private CompletionStatusManager completionStatusManager;
@@ -43,34 +47,42 @@ public static CopilotUi getPlugin() {
@Override
public void start(BundleContext context) throws Exception {
- // wake up Core plugin and wait until copilot LS is ready
- // TODO: check if we can improve logic here, for example, use a listener to wait for LS ready.
- CopilotLanguageServerConnection connection = null;
- for (int i = 0; i < RETRY_COUNT; i++) {
- connection = CopilotCore.getPlugin().getCopilotLanguageServer();
- if (connection != null) {
- break;
+ Job initJob = new Job("Copilot initialization") {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ // wait until Core is initialized.
+ Job.getJobManager().join(CopilotCore.INIT_JOB_FAMILY, null);
+
+ CopilotLanguageServerConnection connection = CopilotCore.getPlugin().getCopilotLanguageServer();
+ if (connection == null) {
+ var ex = new IllegalStateException("Failed to start copilot language server.");
+ LOGGER.log(LogLevel.ERROR, ex);
+ throw ex;
+ }
+
+ CopilotUi.this.editorsManager = new EditorsManager(connection,
+ CopilotCore.getPlugin().getCompletionProvider());
+ CopilotUi.this.editorLifecycleListener = new EditorLifecycleListener(editorsManager);
+ CopilotUi.this.completionStatusManager = new CompletionStatusManager();
+
+ registerPartListener();
+ addCompletionStatusListener();
+
+ // Initialize the completion handler for the active editor in case we miss the event
+ // to initialize it.
+ initCompletionHandlerForActiveEditor();
+ } catch (OperationCanceledException | InterruptedException e) {
+ LOGGER.log(LogLevel.ERROR, e);
+ return Status.error("Failed to initialize GitHub Copilot plugin.", e);
+ }
+ return Status.OK_STATUS;
}
- Thread.sleep(1000);
- }
- if (connection == null) {
- var ex = new IllegalStateException("Failed to start copilot language server.");
- LOGGER.log(LogLevel.ERROR, ex);
- throw ex;
- }
-
- this.editorsManager = new EditorsManager(connection, CopilotCore.getPlugin().getCompletionProvider());
- this.editorLifecycleListener = new EditorLifecycleListener(editorsManager);
- this.completionStatusManager = new CompletionStatusManager();
-
- registerPartListener();
- addCompletionStatusListener();
-
- // Initialize the completion handler for the active editor in case we miss the event
- // to initialize it.
- initCompletionHandlerForActiveEditor();
+ };
+ initJob.setSystem(true);
+ initJob.schedule();
}
-
+
public CompletionStatusManager getCompletionStatusManager() {
return completionStatusManager;
}
From c4d24bf646d7b9bf72e1cd539cd929dc49de9143 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Thu, 26 Dec 2024 13:16:49 +0800
Subject: [PATCH 043/690] fix - UI freeze when completion happens frequently
(#69)
* Previously, we use 'IJobChangeListener' to listen to the job status, side
effect is that when calling 'job.cancel()', it will wait the events to be
notified to all listeners.
Since the completion is triggered from main thread, this behavior will
block the UI. As a workaround, this PR discards the use of 'IJobChangeListener'.
Instead, we make the 'CompletionJob' be the inner class of 'CompletionProvider'
- grammar candy that makes it be able to access the listeners registered in
'CompletionProvider' and notify the listeners directly.
---
.../core/completion/CompletionJobTests.java | 26 +--
.../completion/CompletionProviderTests.java | 31 +++-
.../core/completion/CompletionJob.java | 94 -----------
.../core/completion/CompletionProvider.java | 150 ++++++++++++------
4 files changed, 133 insertions(+), 168 deletions(-)
delete mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionJobTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionJobTests.java
index 5c5cb4ee..8a7493ec 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionJobTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionJobTests.java
@@ -5,7 +5,6 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -20,10 +19,10 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
+import com.microsoft.copilot.eclipse.core.completion.CompletionProvider.CompletionJob;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionDocument;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
-import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
@ExtendWith(MockitoExtension.class)
class CompletionJobTests {
@@ -34,7 +33,7 @@ class CompletionJobTests {
@BeforeEach
public void setUp() {
mockLsConnection = mock(CopilotLanguageServerConnection.class);
- completionJob = new CompletionJob(mockLsConnection);
+ completionJob = mock(CompletionProvider.class).new CompletionJob(mockLsConnection);
}
@Test
@@ -60,23 +59,6 @@ void testCancelCompletionJob() throws InterruptedException {
}
}
- @Test
- void testTriggerCompletionJobWithParams() throws InterruptedException {
- CompletionDocument document = mock(CompletionDocument.class);
- CompletionParams params = new CompletionParams(document);
- completionJob.setCompletionParams(params);
-
- CompletionResult expectedResult = new CompletionResult(new ArrayList<>());
- CompletableFuture future = CompletableFuture.completedFuture(expectedResult);
- when(mockLsConnection.getCompletions(params)).thenReturn(future);
- completionJob.schedule();
- completionJob.join();
-
- IStatus status = completionJob.getResult();
- assertEquals(IStatus.OK, status.getSeverity());
- assertEquals(expectedResult, completionJob.getCompletionResult());
- }
-
@Test
void testShouldTimeoutWhenCompletionTakesTooLong() throws Exception {
when(mockLsConnection.getCompletions(any())).thenAnswer(invocation -> {
@@ -84,7 +66,7 @@ void testShouldTimeoutWhenCompletionTakesTooLong() throws Exception {
return new CompletableFuture<>();
});
- CompletionJob job = new CompletionJob(mockLsConnection);
+ CompletionJob job = new CompletionProvider(mockLsConnection, null).new CompletionJob(mockLsConnection);
Position position = new Position(0, 0);
CompletionDocument completionDoc = new CompletionDocument("file://test.java", position);
completionDoc.setVersion(1);
@@ -94,7 +76,7 @@ void testShouldTimeoutWhenCompletionTakesTooLong() throws Exception {
job.schedule();
IJobManager jobManager = Job.getJobManager();
- jobManager.join(CompletionJob.COMPLETION_JOB_FAMILY, new NullProgressMonitor());
+ jobManager.join(CompletionProvider.COMPLETION_JOB_FAMILY, new NullProgressMonitor());
assertEquals(Status.CANCEL_STATUS, job.getResult());
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionProviderTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionProviderTests.java
index 49243404..48826859 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionProviderTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionProviderTests.java
@@ -1,5 +1,6 @@
package com.microsoft.copilot.eclipse.core.completion;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -7,9 +8,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.jobs.IJobManager;
@@ -17,12 +20,15 @@
import org.eclipse.lsp4j.Position;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionDocument;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
@@ -49,7 +55,7 @@ void testShouldNotifyListenersOnCompletion() throws OperationCanceledException,
Position position = new Position(0, 0);
completionProvider.triggerCompletion("file://test.java", position, 1);
IJobManager jobManager = Job.getJobManager();
- jobManager.join(CompletionJob.COMPLETION_JOB_FAMILY, new NullProgressMonitor());
+ jobManager.join(CompletionProvider.COMPLETION_JOB_FAMILY, new NullProgressMonitor());
verify(mockLsConnection, times(1)).getCompletions(any());
}
@@ -63,5 +69,28 @@ void testShouldNotTriggerCompletionWhenNotSignedIn() throws OperationCanceledExc
completionProvider.triggerCompletion("file://test.java", position, 1);
verify(mockLsConnection, never()).getCompletions(any());
}
+
+ @Test
+ void testTriggerCompletionJobWithParams() throws InterruptedException {
+ when(mockStatusManager.getCopilotStatus()).thenReturn(CopilotStatusResult.OK);
+ CompletionItem mockCompletionItem = mock(CompletionItem.class);
+ when(mockCompletionItem.getUuid()).thenReturn("test");
+ CompletionResult expectedResult = new CompletionResult(List.of(mockCompletionItem));
+ CompletableFuture future = CompletableFuture.completedFuture(expectedResult);
+ when(mockLsConnection.getCompletions(any())).thenReturn(future);
+ CompletionProvider completionProvider = new CompletionProvider(mockLsConnection, mockStatusManager);
+
+ CompletionListener mockListener = mock(CompletionListener.class);
+ completionProvider.addCompletionListener(mockListener);
+
+ completionProvider.triggerCompletion("file://test.java", new Position(0, 0), 1);
+ IJobManager jobManager = Job.getJobManager();
+ jobManager.join(CompletionProvider.COMPLETION_JOB_FAMILY, new NullProgressMonitor());
+
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(CompletionCollection.class);
+ verify(mockListener).onCompletionResolved(argumentCaptor.capture());
+
+ assertEquals("test", argumentCaptor.getValue().getUuids().get(0));
+ }
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java
deleted file mode 100644
index 995b0e4f..00000000
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionJob.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.microsoft.copilot.eclipse.core.completion;
-
-import java.util.Objects;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.core.runtime.jobs.Job;
-
-import com.microsoft.copilot.eclipse.core.Constants;
-import com.microsoft.copilot.eclipse.core.CopilotCore;
-import com.microsoft.copilot.eclipse.core.logger.LogLevel;
-import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
-import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
-import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
-
-/**
- * Job to trigger an inline completion.
- */
-public class CompletionJob extends Job {
-
- /**
- * The job family for completion jobs, can be used to find out this completion job.
- */
- public static final String COMPLETION_JOB_FAMILY = "com.microsoft.copilot.eclipse.completionJobFamily";
-
- private static final int COMPLETION_TIMEOUT_MILLIS = 5000;
-
- private CompletionResult result;
-
- private CopilotLanguageServerConnection lsConnection;
- private CompletionParams params;
-
- /**
- * Creates a new completion job.
- */
- public CompletionJob(CopilotLanguageServerConnection lsConnection) {
- super("Generating completion...");
- this.lsConnection = lsConnection;
- this.setSystem(true);
- this.setPriority(Job.INTERACTIVE);
- }
-
- public void setCompletionParams(CompletionParams params) {
- this.params = params;
- }
-
- public CompletionParams getCompletionParams() {
- return params;
- }
-
- @Override
- protected IStatus run(IProgressMonitor monitor) {
- if (params == null) {
- return new Status(IStatus.ERROR, Constants.PLUGIN_ID, "Invalid completion parameters");
- }
- if (monitor.isCanceled()) {
- return Status.CANCEL_STATUS;
- }
- try {
- this.result = this.lsConnection.getCompletions(params).get(COMPLETION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- return Status.CANCEL_STATUS;
- } catch (ExecutionException e) {
- CopilotCore.LOGGER.log(LogLevel.ERROR, e);
- return new Status(IStatus.ERROR, Constants.PLUGIN_ID, e.getMessage(), e);
- } catch (TimeoutException e) {
- CopilotCore.LOGGER.log(LogLevel.WARNING,
- "Completion request timed out after " + COMPLETION_TIMEOUT_MILLIS + " milliseconds");
- return Status.CANCEL_STATUS;
- }
- if (monitor.isCanceled()) {
- return Status.CANCEL_STATUS;
- }
- return Status.OK_STATUS;
- }
-
- public CompletionResult getCompletionResult() {
- return result;
- }
-
- public String getUriString() {
- return params.getDoc().getUri();
- }
-
- @Override
- public boolean belongsTo(Object family) {
- return Objects.equals(family, COMPLETION_JOB_FAMILY);
- }
-
-}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
index 9da84e08..cc0fa36d 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
@@ -3,13 +3,20 @@
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.jobs.IJobChangeEvent;
-import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.lsp4j.Position;
+import com.microsoft.copilot.eclipse.core.Constants;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionDocument;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
@@ -19,7 +26,12 @@
/**
* Provider for inline completion.
*/
-public class CompletionProvider implements IJobChangeListener {
+public class CompletionProvider {
+
+ /**
+ * The job family for completion jobs, can be used to find out this completion job.
+ */
+ public static final String COMPLETION_JOB_FAMILY = "com.microsoft.copilot.eclipse.completionJobFamily";
private CompletionJob completionJob;
private Set completionListeners;
@@ -32,7 +44,6 @@ public class CompletionProvider implements IJobChangeListener {
public CompletionProvider(CopilotLanguageServerConnection lsConnection, CopilotStatusManager statusManager) {
this.statusManager = statusManager;
this.completionJob = new CompletionJob(lsConnection);
- this.completionJob.addJobChangeListener(this);
this.completionListeners = new LinkedHashSet<>();
this.completionStatusListeners = new LinkedHashSet<>();
}
@@ -49,7 +60,6 @@ public void triggerCompletion(String uriString, Position position, int documentV
return;
}
this.completionJob.cancel();
- this.completionJob.setCompletionParams(null);
CompletionDocument completionDoc = new CompletionDocument(uriString, position);
completionDoc.setVersion(documentVersion);
// following format options are hard-coded, because eclipse support applying the format options
@@ -68,7 +78,7 @@ public void triggerCompletion(String uriString, Position position, int documentV
public void addCompletionListener(CompletionListener listener) {
this.completionListeners.add(listener);
}
-
+
/**
* Register a completion status listener.
*/
@@ -82,7 +92,7 @@ public void addCompletionStatusListener(CompletionStatusListener listener) {
public void removeCompletionListener(CompletionListener listener) {
this.completionListeners.remove(listener);
}
-
+
/**
* Unregister a completion status listener.
*/
@@ -91,61 +101,99 @@ public void removeCompletionStatusListener(CompletionStatusListener listener) {
this.completionStatusListeners.remove(listener);
}
- @Override
- public void aboutToRun(IJobChangeEvent event) {
- for (CompletionStatusListener listener : this.completionStatusListeners) {
- listener.onCompletionAboutToRun();
+ /**
+ * TODO: public for testing.
+ */
+ public class CompletionJob extends Job {
+
+ private static final int COMPLETION_TIMEOUT_MILLIS = 5000;
+
+ private CopilotLanguageServerConnection lsConnection;
+ private CompletionParams params;
+ private CompletionCollection completions;
+
+ /**
+ * Creates a new completion job.
+ */
+ public CompletionJob(CopilotLanguageServerConnection lsConnection) {
+ super("Generating completion...");
+ this.lsConnection = lsConnection;
+ this.setSystem(true);
+ this.setPriority(Job.INTERACTIVE);
}
- }
-
- @Override
- public void awake(IJobChangeEvent event) {
- // do nothing
-
- }
- @Override
- public void done(IJobChangeEvent event) {
- for (CompletionStatusListener listener : this.completionStatusListeners) {
- listener.onCompletionDone();
+ public void setCompletionParams(CompletionParams params) {
+ this.params = params;
}
- IStatus jobStatus = this.completionJob.getResult();
- if (jobStatus != null && !jobStatus.isOK()) {
- return;
- }
- CompletionResult result = this.completionJob.getCompletionResult();
- if (result == null || result.getCompletions() == null || result.getCompletions().isEmpty()) {
- return;
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ notifyCompletionAboutToRun();
+ this.completions = null;
+ try {
+ IStatus status = runCompletion(monitor);
+ if (status.isOK() && this.completions != null) {
+ notifyCompletionResolved();
+ }
+ return status;
+ } finally {
+ notifyCompletionDone();
+ }
}
- CompletionParams params = this.completionJob.getCompletionParams();
- if (params == null) {
- return;
+ private IStatus runCompletion(IProgressMonitor monitor) {
+ if (params == null) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, "Invalid completion parameters");
+ return new Status(IStatus.ERROR, Constants.PLUGIN_ID, "Invalid completion parameters");
+ }
+ if (monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+ try {
+ CompletionResult result = this.lsConnection.getCompletions(params).get(COMPLETION_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+ if (result == null || result.getCompletions() == null || result.getCompletions().isEmpty()) {
+ return Status.OK_STATUS;
+ }
+
+ this.completions = new CompletionCollection(result.getCompletions(), params.getDoc().getUri());
+ } catch (InterruptedException e) {
+ return Status.CANCEL_STATUS;
+ } catch (ExecutionException e) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
+ return new Status(IStatus.ERROR, Constants.PLUGIN_ID, e.getMessage(), e);
+ } catch (TimeoutException e) {
+ CopilotCore.LOGGER.log(LogLevel.WARNING,
+ "Completion request timed out after " + COMPLETION_TIMEOUT_MILLIS + " milliseconds");
+ return Status.CANCEL_STATUS;
+ }
+ if (monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+ return Status.OK_STATUS;
}
- CompletionCollection completions = new CompletionCollection(result.getCompletions(), params.getDoc().getUri());
- for (CompletionListener listener : this.completionListeners) {
- listener.onCompletionResolved(completions);
+ @Override
+ public boolean belongsTo(Object family) {
+ return Objects.equals(family, COMPLETION_JOB_FAMILY);
}
- }
-
- @Override
- public void running(IJobChangeEvent event) {
- // do nothing
-
- }
- @Override
- public void scheduled(IJobChangeEvent event) {
- // do nothing
-
- }
+ private void notifyCompletionAboutToRun() {
+ for (CompletionStatusListener listener : CompletionProvider.this.completionStatusListeners) {
+ listener.onCompletionAboutToRun();
+ }
+ }
- @Override
- public void sleeping(IJobChangeEvent event) {
- // do nothing
+ private void notifyCompletionDone() {
+ for (CompletionStatusListener listener : CompletionProvider.this.completionStatusListeners) {
+ listener.onCompletionDone();
+ }
+ }
+ private void notifyCompletionResolved() {
+ for (CompletionListener listener : CompletionProvider.this.completionListeners) {
+ listener.onCompletionResolved(this.completions);
+ }
+ }
}
-
}
From 8fa2d1c2f5d117f762d89f7cae6d63fdc7475fc4 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Thu, 26 Dec 2024 13:27:43 +0800
Subject: [PATCH 044/690] fix - Plugin localization file not work (#71)
---
com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF | 1 +
1 file changed, 1 insertion(+)
diff --git a/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
index be667e90..8f68ff26 100644
--- a/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
@@ -3,6 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: com.microsoft.copilot.eclipse.ui
Bundle-SymbolicName: com.microsoft.copilot.eclipse.ui;singleton:=true
Bundle-Version: 0.1.0.qualifier
+Bundle-Localization: plugin
Export-Package: com.microsoft.copilot.eclipse.ui,
com.microsoft.copilot.eclipse.ui.completion,
com.microsoft.copilot.eclipse.ui.dialogs,
From c831daf72ca094d442c5268680802e6498ff3f39 Mon Sep 17 00:00:00 2001
From: ethanyhou <149548697+ethanyhou@users.noreply.github.com>
Date: Thu, 26 Dec 2024 19:54:47 +0800
Subject: [PATCH 045/690] feat - Added fallback to the lsp connection provider.
(#70)
---
.gitignore | 3 +-
.../META-INF/MANIFEST.MF | 3 +-
.../build.properties | 3 +-
.../copilot-agent/package.json | 2 +-
.../core/lsp/LsStreamConnectionProvider.java | 131 ++++++++++++++++--
target-platform.target | 7 +
6 files changed, 132 insertions(+), 17 deletions(-)
diff --git a/.gitignore b/.gitignore
index 2e352862..325b9e48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,4 +31,5 @@ replay_pid*
# Copilot agent
**/node_modules/
-**/copilot-agent/native/
\ No newline at end of file
+**/copilot-agent/native/
+**/copilot-agent/dist/
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
index fccf9829..d8fcecc4 100644
--- a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
@@ -21,4 +21,5 @@ Require-Bundle: org.eclipse.lsp4e;bundle-version="0.18.12",
org.apache.commons.lang3;bundle-version="3.17.0",
org.eclipse.jdt.annotation;bundle-version="2.3.0",
org.eclipse.jface.text,
- com.google.gson;bundle-version="2.11.0"
+ com.google.gson;bundle-version="2.11.0",
+ org.eclipse.wildwebdeveloper.embedder.node;bundle-version="1.0.3";resolution:=optional
diff --git a/com.microsoft.copilot.eclipse.core/build.properties b/com.microsoft.copilot.eclipse.core/build.properties
index a19753f5..f892d99a 100644
--- a/com.microsoft.copilot.eclipse.core/build.properties
+++ b/com.microsoft.copilot.eclipse.core/build.properties
@@ -6,5 +6,6 @@ bin.includes = META-INF/,\
copilot-agent/native/darwin-arm64/,\
copilot-agent/native/darwin-x64/,\
copilot-agent/native/linux-x64/,\
- copilot-agent/native/win32-x64/
+ copilot-agent/native/win32-x64/,\
+ copilot-agent/dist/
diff --git a/com.microsoft.copilot.eclipse.core/copilot-agent/package.json b/com.microsoft.copilot.eclipse.core/copilot-agent/package.json
index 0c16fe6a..15a58d57 100644
--- a/com.microsoft.copilot.eclipse.core/copilot-agent/package.json
+++ b/com.microsoft.copilot.eclipse.core/copilot-agent/package.json
@@ -9,7 +9,7 @@
"url": "https://github.com/microsoft/copilot-eclipse.git"
},
"scripts": {
- "postinstall": "npx --yes copyfiles -u 3 \"node_modules/@github/copilot-language-server/native/**/*\" \".\""
+ "postinstall": "npx --yes copyfiles -u 3 \"node_modules/@github/copilot-language-server/native/**/*\" \".\" && npx --yes copyfiles -u 3 \"node_modules/@github/copilot-language-server/dist/**/*\" \".\""
},
"dependencies": {
"@github/copilot-language-server": "^1.244.0"
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
index 50728ac2..e2cb6cd5 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java
@@ -5,15 +5,18 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider;
+import org.eclipse.wildwebdeveloper.embedder.node.NodeJSManager;
import org.osgi.framework.Bundle;
import com.microsoft.copilot.eclipse.core.CopilotCore;
@@ -29,7 +32,6 @@
public class LsStreamConnectionProvider extends ProcessStreamConnectionProvider {
public static final String EDITOR_NAME = "Eclipse";
-
public static final String EDITOR_PLUGIN_NAME = "GitHub Copilot for Eclipse";
@Override
@@ -44,25 +46,128 @@ public Object getInitializationOptions(@Nullable URI rootUri) {
@Override
public void start() throws IOException {
- // load lsp binary
- // call normalize to remove any relative path components and avoid "FILE_PATH_TOO_LONG" error
- Path binary = findBinary().normalize();
+ try {
+ startBinaryLspAgent();
+ } catch (Exception e) {
+ startJsLspAgent(e);
+ }
+ CopilotCore.LOGGER.log(LogLevel.INFO, "Lsp agent started successfully.");
+ }
+
+ private void startBinaryLspAgent() throws IOException {
+ CopilotCore.LOGGER.log(LogLevel.INFO, "Starting language server with binary lsp agent.");
+ this.setCommands(getBinaryLspCommands());
+ super.start();
+ }
+
+ private void startJsLspAgent(Exception e) throws IOException {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, "Binary LSP agent start failed. Retrying with JS agent.", e);
+ this.setCommands(getJavaScriptCommands());
+ super.start();
+ }
+
+ private List getBinaryLspCommands() throws IOException {
+ Path binary = findAndValidateBinary();
+ return buildCommands(binary.toString());
+ }
+
+ private Path findAndValidateBinary() throws IOException {
+
+ Path binary = findBinary();
+
if (binary == null) {
throw new IOException("Could not find the language server binary");
}
+ // call normalize to remove any relative path components and avoid "FILE_PATH_TOO_LONG" error
+ binary = binary.normalize();
File executable = binary.toFile();
- if (!executable.canExecute()) {
- boolean canExecute = executable.setExecutable(true);
- if (!canExecute) {
- // TODO: throw error or handle it?
+ if (!executable.canExecute() && !executable.setExecutable(true)) {
+ throw new IOException("Could not make the language server binary executable");
+ }
+
+ return binary;
+ }
+
+ private List getJavaScriptCommands() throws IOException {
+ try {
+ String nodePath = findNodeAbsolutePath();
+ String jsLspPath = findJavaScriptLanguageServerPath();
+
+ if (nodePath == null) {
+ throw new IOException("Node path not found");
}
+ if (jsLspPath == null) {
+ throw new IOException("JavaScript lsp path not found");
+ }
+
+ return buildCommands(nodePath, jsLspPath);
+ } catch (Exception e) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, "Failed to get JavaScript commands. ", e);
+ throw e;
}
- List commands = new LinkedList<>();
- commands.add(binary.toString());
+ // TODO: In the future, if users have environment variables set up that impact the js server startup, we should
+ // clear the related environment variables here. Reference:
+ // https://github.com/microsoft/copilot-intellij/blob/df3fa9e82ddee36342c50b310be321d552238a30/core/src/main/java/com/github/copilot/lang/agent/CopilotAgentCommandLine.java#L45C4-L54C6
+ }
+
+ private List buildCommands(String... commandParts) {
+ List commands = new ArrayList<>(Arrays.asList(commandParts));
commands.add("--stdio");
- this.setCommands(commands);
- super.start();
+ enforceUtf8Charset(commands);
+ return commands;
+ }
+
+ /**
+ * Enforce UTF-8 charset for the LSP agent commands.
+ */
+ private void enforceUtf8Charset(List commands) {
+ commands.replaceAll(command -> new String(command.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8));
+ }
+
+ private @Nullable String findNodeAbsolutePath() throws IOException {
+ try {
+ // The 'wildwebdeveloper' bundle is optional for Eclipse. Ensure it is available before attempting to use it.
+ Class.forName("org.eclipse.wildwebdeveloper.embedder.node.NodeJSManager");
+ } catch (ClassNotFoundException | NoClassDefFoundError e) {
+ CopilotCore.LOGGER.log(LogLevel.INFO,
+ "Get JavaScript commands aborted. org.eclipse.wildwebdeveloper.embedder.node.NodeJSManager not found.");
+ return null;
+ }
+ File nodeJsLocation = NodeJSManager.getNodeJsLocation();
+ if (nodeJsLocation == null) {
+ throw new IOException("Failed to find Node.js path");
+ }
+ return nodeJsLocation.getAbsolutePath();
+ }
+
+ private @Nullable String findJavaScriptLanguageServerPath() throws IOException {
+ Path distPath = findAgentDistDirectoryPath();
+
+ if (distPath == null) {
+ throw new IOException("Unable to locate dist dir for js language server");
+ }
+
+ Path jsFilePath = distPath.resolve("language-server.js");
+ if (!Files.exists(jsFilePath)) {
+ throw new IOException("Unable to locate language-server.js file");
+ }
+
+ return jsFilePath.toString();
+ }
+
+ private @Nullable Path findAgentDistDirectoryPath() {
+ URL url = CopilotCore.getPlugin().getBundle().getEntry("copilot-agent/dist");
+ if (url == null) {
+ return null;
+ }
+
+ try {
+ return URIUtil.toFile(URIUtil.toURI(FileLocator.toFileURL(url))).toPath();
+ } catch (URISyntaxException | IOException e) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
+ return null;
+ }
}
private @Nullable Path findBinary() throws IOException {
diff --git a/target-platform.target b/target-platform.target
index cf1e2950..4de6fa38 100644
--- a/target-platform.target
+++ b/target-platform.target
@@ -15,6 +15,13 @@
+
+
+
+
+
From ba931cd46c2c8f8ae7d8d2bbf0a7af138e5c3365 Mon Sep 17 00:00:00 2001
From: yanshudan <1397370237@qq.com>
Date: Mon, 30 Dec 2024 16:06:58 +0800
Subject: [PATCH 046/690] feat - Add preference page and auto show completion
pref (#46)
---
.../copilot/eclipse/core/Constants.java | 1 +
.../ui/completion/EditorManagerTests.java | 12 +++--
.../META-INF/MANIFEST.MF | 1 +
.../plugin.properties | 3 +-
com.microsoft.copilot.eclipse.ui/plugin.xml | 54 ++++++++++++-------
.../copilot/eclipse/ui/CopilotUi.java | 6 +--
.../ui/completion/CompletionHandler.java | 27 ++++++++--
.../eclipse/ui/completion/EditorsManager.java | 8 ++-
.../copilot/eclipse/ui/i18n/Messages.java | 2 +
.../eclipse/ui/i18n/messages.properties | 5 +-
.../CopilotPreferenceInitializer.java | 19 +++++++
.../prerferences/CopilotPreferencesPage.java | 41 ++++++++++++++
12 files changed, 145 insertions(+), 34 deletions(-)
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/prerferences/CopilotPreferenceInitializer.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/prerferences/CopilotPreferencesPage.java
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java
index 9894b8e0..95d5479c 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java
@@ -10,4 +10,5 @@ private Constants() {
}
public static final String PLUGIN_ID = "com.microsoft.copilot.eclipse";
+ public static final String AUTO_SHOW_COMPLETION = "enableAutoCompletions";
}
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/EditorManagerTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/EditorManagerTests.java
index 462db6cc..a4338eb8 100644
--- a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/EditorManagerTests.java
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/EditorManagerTests.java
@@ -4,6 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.mock;
+import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.texteditor.ITextEditor;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -22,9 +23,12 @@ class EditorManagerTests {
@Mock
private CompletionProvider mockProvider;
+ @Mock
+ private IPreferenceStore mockPreferenceStore;
+
@Test
void testCreateHandlerForNull() {
- EditorsManager manager = new EditorsManager(mockServer, mockProvider);
+ EditorsManager manager = new EditorsManager(mockServer, mockProvider, mockPreferenceStore);
assertNull(manager.getOrCreateCompletionHandlerFor(null));
}
@@ -33,7 +37,7 @@ void testCreateHandlerForNull() {
void testGetOrCreateCompletionHandlerForReturnsNewHandlerWhenNotPresent() {
ITextEditor mockEditor = mock(ITextEditor.class);
- EditorsManager manager = new EditorsManager(mockServer, mockProvider);
+ EditorsManager manager = new EditorsManager(mockServer, mockProvider, mockPreferenceStore);
CompletionHandler handler = manager.getOrCreateCompletionHandlerFor(mockEditor);
assertNotNull(handler);
@@ -41,7 +45,7 @@ void testGetOrCreateCompletionHandlerForReturnsNewHandlerWhenNotPresent() {
@Test
void testGetActiveHandlerWhenNoActiveEditor() {
- EditorsManager manager = new EditorsManager(mockServer, mockProvider);
+ EditorsManager manager = new EditorsManager(mockServer, mockProvider, mockPreferenceStore);
assertNull(manager.getActiveCompletionHandler());
}
@@ -49,7 +53,7 @@ void testGetActiveHandlerWhenNoActiveEditor() {
@Test
void testGetActiveHandlerWhenActiveEditor() {
ITextEditor mockEditor = mock(ITextEditor.class);
- EditorsManager manager = new EditorsManager(mockServer, mockProvider);
+ EditorsManager manager = new EditorsManager(mockServer, mockProvider, mockPreferenceStore);
manager.getOrCreateCompletionHandlerFor(mockEditor);
manager.setActiveEditor(mockEditor);
diff --git a/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
index 8f68ff26..02653bcf 100644
--- a/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.ui/META-INF/MANIFEST.MF
@@ -9,6 +9,7 @@ Export-Package: com.microsoft.copilot.eclipse.ui,
com.microsoft.copilot.eclipse.ui.dialogs,
com.microsoft.copilot.eclipse.ui.handlers,
com.microsoft.copilot.eclipse.ui.i18n,
+ com.microsoft.copilot.eclipse.ui.prerferences,
com.microsoft.copilot.eclipse.ui.utils
Bundle-Activator: com.microsoft.copilot.eclipse.ui.CopilotUi
Bundle-RequiredExecutionEnvironment: JavaSE-17
diff --git a/com.microsoft.copilot.eclipse.ui/plugin.properties b/com.microsoft.copilot.eclipse.ui/plugin.properties
index 0b65d477..8b7a6b2d 100644
--- a/com.microsoft.copilot.eclipse.ui/plugin.properties
+++ b/com.microsoft.copilot.eclipse.ui/plugin.properties
@@ -4,4 +4,5 @@ command.discardSuggestion.name=Discard Suggestion
command.copilotForEclipsePlugin.name=GitHub Copilot for Eclipse
command.signInToGitHub.name=Sign in to GitHub Copilot
command.signOutFromGitHub.name=Sign out from GitHub Copilot
-command.viewFeedbackForum.name=View Feedback Forum
\ No newline at end of file
+command.viewFeedbackForum.name=View Feedback Forum
+command.preferencesPage.name=GitHub Copilot for Eclipse
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/plugin.xml b/com.microsoft.copilot.eclipse.ui/plugin.xml
index f2786167..dbef6ce2 100644
--- a/com.microsoft.copilot.eclipse.ui/plugin.xml
+++ b/com.microsoft.copilot.eclipse.ui/plugin.xml
@@ -23,21 +23,35 @@
+
+
+
+
+
+
+
+
+ id="com.microsoft.copilot.eclipse.commands.showStatusBarMenu"
+ name="%command.copilotForEclipsePlugin.name">
+ id="com.microsoft.copilot.eclipse.commands.signIn"
+ name="%command.signInToGitHub.name">
+ id="com.microsoft.copilot.eclipse.commands.signOut"
+ name="%command.signOutFromGitHub.name">
-
-
+
+
@@ -65,12 +79,12 @@
commandId="com.microsoft.copilot.eclipse.commands.showStatusBarMenu">
+ class="com.microsoft.copilot.eclipse.ui.handlers.SignInHandler"
+ commandId="com.microsoft.copilot.eclipse.commands.signIn">
+ class="com.microsoft.copilot.eclipse.ui.handlers.SignOutHandler"
+ commandId="com.microsoft.copilot.eclipse.commands.signOut">
-
-
+
+
-
+
@@ -112,4 +126,4 @@
sequence="ESC">
-
+
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
index 4db36271..dcb34d98 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
@@ -3,12 +3,12 @@
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
-import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
import com.microsoft.copilot.eclipse.core.CopilotCore;
@@ -23,7 +23,7 @@
/**
* The plug-in runtime class for the Copilot plug-in containing the UI support, like dialogs, ghost text rendering, etc.
*/
-public class CopilotUi extends Plugin {
+public class CopilotUi extends AbstractUIPlugin {
private static CopilotUi COPILOT_UI_PLUGIN = null;
@@ -62,7 +62,7 @@ protected IStatus run(IProgressMonitor monitor) {
}
CopilotUi.this.editorsManager = new EditorsManager(connection,
- CopilotCore.getPlugin().getCompletionProvider());
+ CopilotCore.getPlugin().getCompletionProvider(), getPreferenceStore());
CopilotUi.this.editorLifecycleListener = new EditorLifecycleListener(editorsManager);
CopilotUi.this.completionStatusManager = new CompletionStatusManager();
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
index 75749bef..e2bfc4e8 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
@@ -3,6 +3,7 @@
import java.io.IOException;
import java.net.URI;
+import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DefaultPositionUpdater;
@@ -10,11 +11,14 @@
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextSelection;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.swt.custom.CaretEvent;
import org.eclipse.swt.custom.CaretListener;
import org.eclipse.ui.texteditor.ITextEditor;
+import com.microsoft.copilot.eclipse.core.Constants;
import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
@@ -27,7 +31,7 @@
* A class to listen events which are completion related and notify the completion manager to render the ghost text or
* apply the suggestion to document.
*/
-public class CompletionHandler implements CaretListener {
+public class CompletionHandler implements CaretListener, IPropertyChangeListener {
private CopilotLanguageServerConnection lsConnection;
private CompletionProvider provider;
@@ -39,12 +43,14 @@ public class CompletionHandler implements CaretListener {
private DefaultPositionUpdater positionUpdater;
private CompletionManager completionManager;
+ private boolean autoShowCompletion;
+ private IPreferenceStore preferenceStore;
/**
* Creates a new completion handler.
*/
public CompletionHandler(CopilotLanguageServerConnection lsConnection, CompletionProvider provider,
- ITextEditor editor) {
+ ITextEditor editor, IPreferenceStore preferenceStore) {
this.lsConnection = lsConnection;
this.textViewer = (ITextViewer) editor.getAdapter(ITextOperationTarget.class);
// if the text viewer is null, we will not register listeners.
@@ -82,6 +88,11 @@ public CompletionHandler(CopilotLanguageServerConnection lsConnection, Completio
this.positionUpdater = new DefaultPositionUpdater(this.getCategory());
this.document.addPositionCategory(this.getCategory());
this.document.addPositionUpdater(this.positionUpdater);
+
+ // initialize the auto show completion preference and add listener to update it.
+ this.preferenceStore = preferenceStore;
+ this.autoShowCompletion = preferenceStore.getBoolean(Constants.AUTO_SHOW_COMPLETION);
+ preferenceStore.addPropertyChangeListener(this);
}
/**
@@ -154,11 +165,20 @@ public void caretMoved(CaretEvent event) {
clearCompletionRendering();
} else {
this.documentVersion = currentVersion;
- triggerCompletion();
+ if (this.autoShowCompletion) {
+ triggerCompletion();
+ }
}
}
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(Constants.AUTO_SHOW_COMPLETION)) {
+ this.autoShowCompletion = Boolean.parseBoolean(event.getNewValue().toString());
+ }
+ }
+
/**
* Get category for the position updater of this document.
*/
@@ -177,6 +197,7 @@ public void dispose() {
this.completionManager.dispose();
this.completionManager = null;
+ preferenceStore.removePropertyChangeListener(this);
lsConnection.disconnectDocument(this.documentUri);
try {
this.document.removePositionCategory(this.getCategory());
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorsManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorsManager.java
index 25870934..6a042971 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorsManager.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorsManager.java
@@ -5,6 +5,7 @@
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.texteditor.ITextEditor;
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
@@ -19,15 +20,18 @@ public class EditorsManager {
private CompletionProvider completionProvider;
private Map editorMap;
private AtomicReference activeEditor;
+ private IPreferenceStore preferenceStore;
/**
* Creates a new EditorManager.
*/
- public EditorsManager(CopilotLanguageServerConnection languageServer, CompletionProvider completionProvider) {
+ public EditorsManager(CopilotLanguageServerConnection languageServer, CompletionProvider completionProvider,
+ IPreferenceStore preferenceStore) {
this.languageServer = languageServer;
this.completionProvider = completionProvider;
this.editorMap = new ConcurrentHashMap<>();
this.activeEditor = new AtomicReference<>();
+ this.preferenceStore = preferenceStore;
}
/**
@@ -41,7 +45,7 @@ public CompletionHandler getOrCreateCompletionHandlerFor(ITextEditor editor) {
}
return editorMap.computeIfAbsent(editor,
- edt -> new CompletionHandler(this.languageServer, this.completionProvider, edt));
+ edt -> new CompletionHandler(this.languageServer, this.completionProvider, edt, this.preferenceStore));
}
/**
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
index 02f42a04..62924551 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
@@ -41,6 +41,8 @@ public final class Messages extends NLS {
public static String signOutHandler_msgDialog_signOutSuccess;
public static String signOutHandler_msgDialog_signOutFailed;
public static String signOutHandler_msgDialog_signOutFailedFailure;
+ public static String preferencesPage_description;
+ public static String preferencesPage_autoShowCompletion;
static {
// initialize resource bundle
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
index 52e9adb9..99559d83 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
@@ -31,4 +31,7 @@ signInHandler_msgDialog_signInFailedFailure=Copilot Sign In Failure
signOutHandler_msgDialog_githubCopilot=GitHub Copilot
signOutHandler_msgDialog_signOutSuccess=You have successfully signed out from Copilot.
signOutHandler_msgDialog_signOutFailed=Unable to sign out to GitHub Copilot at this time
-signOutHandler_msgDialog_signOutFailedFailure=Copilot Sign Out Failure
\ No newline at end of file
+signOutHandler_msgDialog_signOutFailedFailure=Copilot Sign Out Failure
+
+preferencesPage_description=Configure GitHub Copilot for Eclipse
+preferencesPage_autoShowCompletion=Automatically show inline completions
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/prerferences/CopilotPreferenceInitializer.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/prerferences/CopilotPreferenceInitializer.java
new file mode 100644
index 00000000..b1ea307a
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/prerferences/CopilotPreferenceInitializer.java
@@ -0,0 +1,19 @@
+package com.microsoft.copilot.eclipse.ui.prerferences;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import com.microsoft.copilot.eclipse.core.Constants;
+import com.microsoft.copilot.eclipse.ui.CopilotUi;
+
+/**
+ * A class to initialize the default preferences for the plugin.
+ */
+public class CopilotPreferenceInitializer extends AbstractPreferenceInitializer {
+
+ @Override
+ public void initializeDefaultPreferences() {
+ IPreferenceStore pref = CopilotUi.getPlugin().getPreferenceStore();
+ pref.setDefault(Constants.AUTO_SHOW_COMPLETION, true);
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/prerferences/CopilotPreferencesPage.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/prerferences/CopilotPreferencesPage.java
new file mode 100644
index 00000000..65ddf1eb
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/prerferences/CopilotPreferencesPage.java
@@ -0,0 +1,41 @@
+package com.microsoft.copilot.eclipse.ui.prerferences;
+
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.preferences.ScopedPreferenceStore;
+
+import com.microsoft.copilot.eclipse.core.Constants;
+import com.microsoft.copilot.eclipse.ui.CopilotUi;
+import com.microsoft.copilot.eclipse.ui.i18n.Messages;
+
+/**
+ * This class is used to create the preference page for the plugin.
+ */
+public class CopilotPreferencesPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
+
+ /**
+ * Constructor.
+ */
+ public CopilotPreferencesPage() {
+ super(GRID);
+ }
+
+ @Override
+ public void createFieldEditors() {
+ var parent = getFieldEditorParent();
+ var editorGroup = new Composite(parent, 0);
+ addField(new BooleanFieldEditor(Constants.AUTO_SHOW_COMPLETION, Messages.preferencesPage_autoShowCompletion,
+ (Composite) editorGroup));
+ }
+
+ @Override
+ public void init(IWorkbench workbench) {
+ // second parameter is typically the plug-in id
+ setPreferenceStore(CopilotUi.getPlugin().getPreferenceStore());
+ }
+
+}
\ No newline at end of file
From c4a8e19258767351e1891353b0dfe85818ddf5c0 Mon Sep 17 00:00:00 2001
From: ethanyhou <149548697+ethanyhou@users.noreply.github.com>
Date: Mon, 30 Dec 2024 19:45:52 +0800
Subject: [PATCH 047/690] fix - Add signin signout listener and fix auth
related bugs. (#76)
---
...Tests.java => AuthStatusManagerTests.java} | 18 +--
.../core/completion/CompletionJobTests.java | 34 ++++
.../completion/CompletionProviderTests.java | 4 +-
...tusManager.java => AuthStatusManager.java} | 73 ++++++---
.../core/CopilotAuthStatusListener.java | 14 ++
.../copilot/eclipse/core/CopilotCore.java | 12 +-
.../core/completion/CompletionProvider.java | 7 +-
.../lsp/CopilotLanguageServerConnection.java | 8 +-
...Manager.java => CopilotStatusManager.java} | 13 +-
.../copilot/eclipse/ui/CopilotUi.java | 25 ++-
.../ui/dialogs/SignInConfirmDialog.java | 2 +-
.../eclipse/ui/handlers/CopilotHandler.java | 6 +-
.../ui/handlers/ShowStatusBarMenuHandler.java | 22 +--
.../eclipse/ui/handlers/SignInHandler.java | 147 +++++++++++-------
.../eclipse/ui/handlers/SignOutHandler.java | 10 +-
15 files changed, 262 insertions(+), 133 deletions(-)
rename com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/{CopilotStatusManagerTests.java => AuthStatusManagerTests.java} (78%)
rename com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/{CopilotStatusManager.java => AuthStatusManager.java} (55%)
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotAuthStatusListener.java
rename com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/{completion/CompletionStatusManager.java => CopilotStatusManager.java} (58%)
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
similarity index 78%
rename from com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java
rename to com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
index dabf9401..d633290f 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/CopilotStatusManagerTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/AuthStatusManagerTests.java
@@ -16,15 +16,15 @@
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
@ExtendWith(MockitoExtension.class)
-class CopilotStatusManagerTests {
+class AuthStatusManagerTests {
@Mock
CopilotLanguageServerConnection mockConnection;
- CopilotStatusManager copilotStatusManager;
+ AuthStatusManager authStatusManager;
@BeforeEach
public void setUp() {
- copilotStatusManager = new CopilotStatusManager(mockConnection);
+ authStatusManager = new AuthStatusManager(mockConnection);
}
@Test
@@ -33,9 +33,9 @@ void testCopilotStatusResultOnSuccess() {
expectedResult.setStatus(CopilotStatusResult.OK);
when(mockConnection.checkStatus(false)).thenReturn(CompletableFuture.completedFuture(expectedResult));
- copilotStatusManager.checkStatus();
+ authStatusManager.checkStatus();
- assertEquals(CopilotStatusResult.OK, copilotStatusManager.getCopilotStatus());
+ assertEquals(CopilotStatusResult.OK, authStatusManager.getCopilotStatus());
}
@Test
@@ -49,10 +49,10 @@ void testCheckStatusOK() throws InterruptedException {
when(mockConnection.checkStatus(false)).thenReturn(future);
future.complete(expectedResult);
- copilotStatusManager.checkStatus();
+ authStatusManager.checkStatus();
// Assert final status is OK
- assertEquals(CopilotStatusResult.OK, copilotStatusManager.getCopilotStatus());
+ assertEquals(CopilotStatusResult.OK, authStatusManager.getCopilotStatus());
}
@Test
@@ -62,9 +62,9 @@ void testCheckStatusError() {
when(mockConnection.checkStatus(false)).thenReturn(future);
- copilotStatusManager.checkStatus();
+ authStatusManager.checkStatus();
- assertEquals(CopilotStatusResult.ERROR, copilotStatusManager.getCopilotStatus());
+ assertEquals(CopilotStatusResult.ERROR, authStatusManager.getCopilotStatus());
}
}
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionJobTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionJobTests.java
index 8a7493ec..fe58f5d7 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionJobTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionJobTests.java
@@ -1,11 +1,13 @@
package com.microsoft.copilot.eclipse.core.completion;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.IStatus;
@@ -19,10 +21,13 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
+import com.microsoft.copilot.eclipse.core.AuthStatusManager;
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider.CompletionJob;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionDocument;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
@ExtendWith(MockitoExtension.class)
class CompletionJobTests {
@@ -81,5 +86,34 @@ void testShouldTimeoutWhenCompletionTakesTooLong() throws Exception {
assertEquals(Status.CANCEL_STATUS, job.getResult());
}
+
+
+ @Test
+ void testTriggerCompletionJobWhenCopilotIsSignedOutNotUsingEclipse() throws InterruptedException {
+ CopilotStatusResult expectedResult = new CopilotStatusResult();
+ expectedResult.setStatus(CopilotStatusResult.ERROR);
+ AuthStatusManager authStatusManager = mock(AuthStatusManager.class);
+ when(authStatusManager.setCopilotStatus(CopilotStatusResult.ERROR)).thenReturn(expectedResult);
+ when(authStatusManager.getCopilotStatus()).thenReturn(CopilotStatusResult.ERROR);
+ CompletableFuture future = new CompletableFuture<>();
+ future.completeExceptionally(new ExecutionException("Not signed in", new Throwable()));
+ when(mockLsConnection.getCompletions(any())).thenReturn(future);
+
+ CompletionJob job = new CompletionProvider(mockLsConnection, authStatusManager).new CompletionJob(mockLsConnection);
+ Position position = new Position(0, 0);
+ CompletionDocument completionDoc = new CompletionDocument("file://test.java", position);
+ completionDoc.setVersion(1);
+ completionDoc.setInsertSpaces(true);
+ completionDoc.setTabSize(4);
+ job.setCompletionParams(new CompletionParams(completionDoc));
+ job.schedule();
+
+ IJobManager jobManager = Job.getJobManager();
+ jobManager.join(CompletionProvider.COMPLETION_JOB_FAMILY, new NullProgressMonitor());
+
+ assertTrue(job.getResult().getMessage().contains("Not signed in"));
+ assertEquals(IStatus.ERROR, job.getResult().getSeverity());
+ assertEquals(CopilotStatusResult.ERROR, authStatusManager.getCopilotStatus());
+ }
}
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionProviderTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionProviderTests.java
index 48826859..b95ea40d 100644
--- a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionProviderTests.java
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/completion/CompletionProviderTests.java
@@ -24,7 +24,7 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
-import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
+import com.microsoft.copilot.eclipse.core.AuthStatusManager;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionDocument;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
@@ -39,7 +39,7 @@ class CompletionProviderTests {
private CopilotLanguageServerConnection mockLsConnection;
@Mock
- private CopilotStatusManager mockStatusManager;
+ private AuthStatusManager mockStatusManager;
@Mock
private CompletionListener mockListener;
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/AuthStatusManager.java
similarity index 55%
rename from com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
rename to com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/AuthStatusManager.java
index 7f379ea8..7ed3a1ab 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotStatusManager.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/AuthStatusManager.java
@@ -1,5 +1,8 @@
package com.microsoft.copilot.eclipse.core;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
@@ -10,21 +13,22 @@
/**
* Manager for the authentication status.
*/
-public class CopilotStatusManager {
+public class AuthStatusManager {
private CopilotLanguageServerConnection connection;
-
+ private Set copilotAuthStatusListeners;
private CopilotStatusResult copilotStatusResult;
/**
- * Constructor for the CopilotStatusManager.
+ * Constructor for the AuthStatusManager.
*
* @param connection the connection to the language server.
*/
- public CopilotStatusManager(CopilotLanguageServerConnection connection) {
+ public AuthStatusManager(CopilotLanguageServerConnection connection) {
this.connection = connection;
+ this.copilotAuthStatusListeners = new LinkedHashSet<>();
this.copilotStatusResult = new CopilotStatusResult();
- this.copilotStatusResult.setStatus(CopilotStatusResult.OK);
+ setCopilotStatus(CopilotStatusResult.LOADING);
}
/**
@@ -36,8 +40,9 @@ public CopilotStatusManager(CopilotLanguageServerConnection connection) {
public SignInInitiateResult signInInitiate() throws InterruptedException, ExecutionException {
SignInInitiateResult result = connection.signInInitiate().get();
if (result.isAlreadySignedIn()) {
- this.copilotStatusResult.setStatus(CopilotStatusResult.OK);
+ setCopilotStatus(CopilotStatusResult.OK);
}
+
return result;
}
@@ -50,9 +55,10 @@ public SignInInitiateResult signInInitiate() throws InterruptedException, Execut
public CopilotStatusResult signInConfirm(String userCode) throws InterruptedException, ExecutionException {
CopilotStatusResult result = connection.signInConfirm(userCode).get();
if (result.isSignedIn()) {
- this.copilotStatusResult.setStatus(CopilotStatusResult.OK);
this.copilotStatusResult.setUser(result.getUser());
}
+
+ setCopilotStatus(result.getStatus());
return result;
}
@@ -64,30 +70,33 @@ public CopilotStatusResult signInConfirm(String userCode) throws InterruptedExce
*/
public CopilotStatusResult signOut() throws InterruptedException, ExecutionException {
CopilotStatusResult result = connection.signOut().get();
- if (!result.isSignedIn()) {
- this.copilotStatusResult.setStatus(CopilotStatusResult.NOT_SIGNED_IN);
- }
+ setCopilotStatus(result.getStatus());
return result;
}
-
+
/**
- * Set the status to OK.
+ * Set the CopilotStatusResult string to the given status and notify the listeners.
*/
- public CopilotStatusResult setCompletionDone() {
- this.copilotStatusResult.setStatus(CopilotStatusResult.OK);
+ public CopilotStatusResult setCopilotStatus(String newCopilotStatusResult) {
+ if (!Objects.equals(this.copilotStatusResult.getStatus(), newCopilotStatusResult)) {
+ this.copilotStatusResult.setStatus(newCopilotStatusResult);
+ onDidCopilotStatusChange(this.copilotStatusResult);
+ }
return this.copilotStatusResult;
}
/**
- * Check the login status for current machine.
+ * Check the authentication status for current machine.
*/
public void checkStatus() {
- this.connection.checkStatus(false).thenAccept(result -> {
- this.copilotStatusResult = result;
- }).exceptionally(ex -> {
- CopilotCore.LOGGER.log(LogLevel.ERROR, ex);
- this.copilotStatusResult.setStatus(CopilotStatusResult.ERROR);
-
+ this.connection.checkStatus(false).handle((result, ex) -> {
+ if (ex != null) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, ex);
+ setCopilotStatus(CopilotStatusResult.ERROR);
+ } else {
+ setCopilotStatus(result.getStatus());
+ }
+ onDidCopilotStatusChange(this.copilotStatusResult);
return null;
});
}
@@ -101,4 +110,26 @@ public String getCopilotStatus() {
}
return this.copilotStatusResult.getStatus();
}
+
+ /**
+ * Add a listener for the authentication status.
+ */
+ public void addCopilotAuthStatusListener(CopilotAuthStatusListener listener) {
+ this.copilotAuthStatusListeners.add(listener);
+ }
+
+ /**
+ * Remove the listener for the authentication status.
+ */
+ public void removeCopilotAuthStatusListener(CopilotAuthStatusListener listener) {
+ this.copilotAuthStatusListeners.remove(listener);
+ }
+
+ private void onDidCopilotStatusChange(CopilotStatusResult copilotStatusResult) {
+ if (!this.copilotAuthStatusListeners.isEmpty()) {
+ for (CopilotAuthStatusListener listener : this.copilotAuthStatusListeners) {
+ listener.onDidCopilotStatusChange(copilotStatusResult);
+ }
+ }
+ }
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotAuthStatusListener.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotAuthStatusListener.java
new file mode 100644
index 00000000..b63f966a
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotAuthStatusListener.java
@@ -0,0 +1,14 @@
+package com.microsoft.copilot.eclipse.core;
+
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
+
+/**
+ * Listener for the authentication status.
+ */
+public interface CopilotAuthStatusListener {
+
+ /**
+ * Notifies to the listeners when the authentication status is changed.
+ */
+ void onDidCopilotStatusChange(CopilotStatusResult copilotStatusResult);
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
index aeb37af4..cad2f071 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
@@ -24,7 +24,7 @@
public class CopilotCore extends Plugin {
private CopilotLanguageServerConnection copilotLanguageServer;
- private CopilotStatusManager copilotStatusManager;
+ private AuthStatusManager authStatusManager;
private CompletionProvider completionProvider;
private static CopilotCore COPILOT_CORE_PLUGIN = null;
@@ -74,10 +74,10 @@ void init() {
LanguageServerWrapper wrapper = LanguageServiceAccessor.startLanguageServer(serverDef);
this.copilotLanguageServer = new CopilotLanguageServerConnection(wrapper);
- this.copilotStatusManager = new CopilotStatusManager(this.copilotLanguageServer);
- this.completionProvider = new CompletionProvider(this.copilotLanguageServer, copilotStatusManager);
+ this.authStatusManager = new AuthStatusManager(this.copilotLanguageServer);
+ this.completionProvider = new CompletionProvider(this.copilotLanguageServer, authStatusManager);
- this.copilotStatusManager.checkStatus();
+ this.authStatusManager.checkStatus();
};
Job initJob = new Job("GitHub Copilot Initialization...") {
@@ -99,8 +99,8 @@ public CopilotLanguageServerConnection getCopilotLanguageServer() {
return copilotLanguageServer;
}
- public CopilotStatusManager getCopilotStatusManager() {
- return copilotStatusManager;
+ public AuthStatusManager getAuthStatusManager() {
+ return authStatusManager;
}
public CompletionProvider getCompletionProvider() {
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
index cc0fa36d..15fae504 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/completion/CompletionProvider.java
@@ -13,9 +13,9 @@
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.lsp4j.Position;
+import com.microsoft.copilot.eclipse.core.AuthStatusManager;
import com.microsoft.copilot.eclipse.core.Constants;
import com.microsoft.copilot.eclipse.core.CopilotCore;
-import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionDocument;
@@ -36,12 +36,12 @@ public class CompletionProvider {
private CompletionJob completionJob;
private Set completionListeners;
private Set completionStatusListeners;
- private CopilotStatusManager statusManager;
+ private AuthStatusManager statusManager;
/**
* Creates a new completion provider.
*/
- public CompletionProvider(CopilotLanguageServerConnection lsConnection, CopilotStatusManager statusManager) {
+ public CompletionProvider(CopilotLanguageServerConnection lsConnection, AuthStatusManager statusManager) {
this.statusManager = statusManager;
this.completionJob = new CompletionJob(lsConnection);
this.completionListeners = new LinkedHashSet<>();
@@ -160,6 +160,7 @@ private IStatus runCompletion(IProgressMonitor monitor) {
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
} catch (ExecutionException e) {
+ statusManager.setCopilotStatus(CopilotStatusResult.ERROR);
CopilotCore.LOGGER.log(LogLevel.ERROR, e);
return new Status(IStatus.ERROR, Constants.PLUGIN_ID, e.getMessage(), e);
} catch (TimeoutException e) {
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
index c565a8bf..2b7078d1 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
@@ -9,7 +9,7 @@
import org.eclipse.lsp4e.LanguageServerWrapper;
import org.eclipse.lsp4j.services.LanguageServer;
-import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
+import com.microsoft.copilot.eclipse.core.AuthStatusManager;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CheckStatusParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionResult;
@@ -84,7 +84,7 @@ public CompletableFuture getCompletions(CompletionParams param
}
/**
- * Please use the {@link CopilotStatusManager#signInInitiate()} method instead.
+ * Please use the {@link AuthStatusManager#signInInitiate()} method instead.
*
* Initiate the sign in process.
*/
@@ -95,7 +95,7 @@ public CompletableFuture signInInitiate() {
}
/**
- * Please use the {@link CopilotStatusManager#signInConfirm()} method instead.
+ * Please use the {@link AuthStatusManager#signInConfirm()} method instead.
*
* Confirm the sign in process.
*/
@@ -108,7 +108,7 @@ public CompletableFuture signInConfirm(String userCode) {
}
/**
- * Please use the {@link CopilotStatusManager#signOut()} method instead.
+ * Please use the {@link AuthStatusManager#signOut()} method instead.
*
* Sign out from the GitHub Copilot.
*/
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotStatusManager.java
similarity index 58%
rename from com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusManager.java
rename to com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotStatusManager.java
index 30ef37c1..df1ea8f5 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionStatusManager.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotStatusManager.java
@@ -1,19 +1,21 @@
-package com.microsoft.copilot.eclipse.ui.completion;
+package com.microsoft.copilot.eclipse.ui;
+import com.microsoft.copilot.eclipse.core.CopilotAuthStatusListener;
import com.microsoft.copilot.eclipse.core.completion.CompletionStatusListener;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
/**
* Listener for tracking copilot completion status.
*/
-public class CompletionStatusManager implements CompletionStatusListener {
+public class CopilotStatusManager implements CompletionStatusListener, CopilotAuthStatusListener {
private boolean completionInProgress;
/**
* Constructor for the CompletionStatusManager.
*/
- public CompletionStatusManager() {
+ public CopilotStatusManager() {
}
@Override
@@ -31,4 +33,9 @@ public void onCompletionDone() {
public boolean isCompletionInProgress() {
return completionInProgress;
}
+
+ @Override
+ public void onDidCopilotStatusChange(CopilotStatusResult copilotStatusResult) {
+ UiUtils.refreshCopilotMenu();
+ }
}
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
index dcb34d98..84dbfd49 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
@@ -15,7 +15,6 @@
import com.microsoft.copilot.eclipse.core.logger.CopilotForEclipseLogger;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
-import com.microsoft.copilot.eclipse.ui.completion.CompletionStatusManager;
import com.microsoft.copilot.eclipse.ui.completion.EditorLifecycleListener;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
@@ -27,7 +26,7 @@ public class CopilotUi extends AbstractUIPlugin {
private static CopilotUi COPILOT_UI_PLUGIN = null;
- private CompletionStatusManager completionStatusManager;
+ private CopilotStatusManager copilotStatusManager;
private EditorLifecycleListener editorLifecycleListener;
private EditorsManager editorsManager;
public static final CopilotForEclipseLogger LOGGER = new CopilotForEclipseLogger(CopilotCore.class.getName());
@@ -64,10 +63,11 @@ protected IStatus run(IProgressMonitor monitor) {
CopilotUi.this.editorsManager = new EditorsManager(connection,
CopilotCore.getPlugin().getCompletionProvider(), getPreferenceStore());
CopilotUi.this.editorLifecycleListener = new EditorLifecycleListener(editorsManager);
- CopilotUi.this.completionStatusManager = new CompletionStatusManager();
+ CopilotUi.this.copilotStatusManager = new CopilotStatusManager();
registerPartListener();
addCompletionStatusListener();
+ addCopilotAuthStatusListener();
// Initialize the completion handler for the active editor in case we miss the event
// to initialize it.
@@ -83,14 +83,16 @@ protected IStatus run(IProgressMonitor monitor) {
initJob.schedule();
}
- public CompletionStatusManager getCompletionStatusManager() {
- return completionStatusManager;
+ public CopilotStatusManager getAuthAndCompletionStatusManager() {
+ return copilotStatusManager;
}
@Override
public void stop(BundleContext context) throws Exception {
unregisterPartListener();
removeCompletionStatusListener();
+ removeCopilotAuthStatusListener();
+
if (this.editorsManager != null) {
this.editorsManager.dispose();
}
@@ -107,8 +109,12 @@ private void registerPartListener() {
}
}
+ private void addCopilotAuthStatusListener() {
+ CopilotCore.getPlugin().getAuthStatusManager().addCopilotAuthStatusListener(this.copilotStatusManager);
+ }
+
private void addCompletionStatusListener() {
- CopilotCore.getPlugin().getCompletionProvider().addCompletionStatusListener(this.completionStatusManager);
+ CopilotCore.getPlugin().getCompletionProvider().addCompletionStatusListener(this.copilotStatusManager);
}
private void initCompletionHandlerForActiveEditor() {
@@ -126,7 +132,12 @@ private void unregisterPartListener() {
}
private void removeCompletionStatusListener() {
- CopilotCore.getPlugin().getCompletionProvider().removeCompletionStatusListener(this.completionStatusManager);
+ CopilotCore.getPlugin().getCompletionProvider().removeCompletionStatusListener(this.copilotStatusManager);
+ }
+
+ private void removeCopilotAuthStatusListener() {
+ CopilotCore.getPlugin().getAuthStatusManager()
+ .removeCopilotAuthStatusListener(this.copilotStatusManager);
}
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
index c5558fc1..c28b40a6 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
@@ -75,7 +75,7 @@ public void run(IProgressMonitor monitor) throws InvocationTargetException, Inte
try {
future = CompletableFuture.supplyAsync(() -> {
try {
- return CopilotCore.getPlugin().getCopilotStatusManager().signInConfirm(userCode);
+ return CopilotCore.getPlugin().getAuthStatusManager().signInConfirm(userCode);
} catch (Exception e) {
CopilotUi.LOGGER.log(LogLevel.ERROR, e);
return null;
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java
index b3692fea..be347e3b 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java
@@ -5,9 +5,9 @@
import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.ui.CopilotStatusManager;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
-import com.microsoft.copilot.eclipse.ui.completion.CompletionStatusManager;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
/**
@@ -34,7 +34,7 @@ public CopilotLanguageServerConnection getLanguageServerConnection() {
return CopilotCore.getPlugin().getCopilotLanguageServer();
}
- public CompletionStatusManager getCompletionStatusManager() {
- return CopilotUi.getPlugin().getCompletionStatusManager();
+ public CopilotStatusManager getAuthAndCompletionStatusManager() {
+ return CopilotUi.getPlugin().getAuthAndCompletionStatusManager();
}
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
index 5ce7a299..43e0dcf6 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
@@ -21,12 +21,12 @@
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.menus.UIElement;
+import com.microsoft.copilot.eclipse.core.AuthStatusManager;
import com.microsoft.copilot.eclipse.core.CopilotCore;
-import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
+import com.microsoft.copilot.eclipse.ui.CopilotStatusManager;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
-import com.microsoft.copilot.eclipse.ui.completion.CompletionStatusManager;
import com.microsoft.copilot.eclipse.ui.i18n.Messages;
import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
@@ -35,13 +35,13 @@
*/
public class ShowStatusBarMenuHandler extends CopilotHandler implements IElementUpdater {
private IHandlerService handlerService;
- private CopilotStatusManager copilotStatusManager;
+ private AuthStatusManager authStatusManager;
private SpinnerJob spinnerJob;
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
handlerService = HandlerUtil.getActiveWorkbenchWindow(event).getService(IHandlerService.class);
- copilotStatusManager = CopilotCore.getPlugin().getCopilotStatusManager();
+ authStatusManager = CopilotCore.getPlugin().getAuthStatusManager();
MenuManager menuManager = new MenuManager();
// Sign in status section
@@ -54,7 +54,7 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
// Sign in & sign out section
menuManager.add(new Separator());
- if (!Objects.equals(copilotStatusManager.getCopilotStatus(), CopilotStatusResult.LOADING)) {
+ if (!Objects.equals(authStatusManager.getCopilotStatus(), CopilotStatusResult.LOADING)) {
addSignInOrSignOutAction(menuManager);
}
@@ -66,7 +66,7 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
@Override
public void updateElement(UIElement element, Map parameters) {
- CompletionStatusManager completionStatusManager = getCompletionStatusManager();
+ CopilotStatusManager completionStatusManager = getAuthAndCompletionStatusManager();
if (completionStatusManager.isCompletionInProgress()) {
scheduleSpinnerJob(element);
@@ -76,7 +76,7 @@ public void updateElement(UIElement element, Map parameters) {
spinnerJob.cancel();
}
- String copilotStatus = CopilotCore.getPlugin().getCopilotStatusManager().getCopilotStatus();
+ String copilotStatus = CopilotCore.getPlugin().getAuthStatusManager().getCopilotStatus();
String iconPath = null;
switch (copilotStatus) {
@@ -104,7 +104,7 @@ public void updateElement(UIElement element, Map parameters) {
}
private void addStatusAction(MenuManager menuManager) {
- String copilotStatus = getCopilotStatusBasedOnAuthAndCompletionResult(copilotStatusManager.getCopilotStatus());
+ String copilotStatus = getCopilotStatusBasedOnAuthAndCompletionResult(authStatusManager.getCopilotStatus());
String copilotStatusTitle = Messages.menu_copilotStatus + ": " + copilotStatus;
MenuActionFactory.createMenuAction(menuManager, copilotStatusTitle, handlerService, copilotStatus, false);
@@ -116,7 +116,7 @@ private void addLinkToFeedbackForumAction(MenuManager menuManager) {
}
private String getCopilotStatusBasedOnAuthAndCompletionResult(String copilotStatus) {
- CompletionStatusManager completionStatusManager = getCompletionStatusManager();
+ CopilotStatusManager completionStatusManager = getAuthAndCompletionStatusManager();
switch (copilotStatus) {
case CopilotStatusResult.OK:
return completionStatusManager.isCompletionInProgress() ? Messages.menu_copilotStatus_completionInProgress
@@ -147,7 +147,7 @@ private void scheduleSpinnerJob(UIElement uiElement) {
}
private void addSignInOrSignOutAction(MenuManager menuManager) {
- if (Objects.equals(copilotStatusManager.getCopilotStatus(), CopilotStatusResult.OK)) {
+ if (Objects.equals(authStatusManager.getCopilotStatus(), CopilotStatusResult.OK)) {
ImageDescriptor signInIcon = UiUtils.buildImageDescriptorFromPngPath("/icons/signin.png");
MenuActionFactory.createMenuAction(menuManager, Messages.menu_signOutFromGitHub, signInIcon, handlerService,
"com.microsoft.copilot.eclipse.commands.signOut", true);
@@ -209,7 +209,7 @@ protected IStatus run(IProgressMonitor monitor) {
ImageDescriptor newIcon = UiUtils.buildImageDescriptorFromPngPath(iconPath);
this.uiElement.setIcon(newIcon);
currentIconIndex = (currentIconIndex % TOTAL_SPINNER_ICONS) + 1;
- if (CopilotUi.getPlugin().getCompletionStatusManager().isCompletionInProgress()) {
+ if (CopilotUi.getPlugin().getAuthAndCompletionStatusManager().isCompletionInProgress()) {
schedule(COMPLETION_IN_PROGRESS_SPINNER_ROTATE_RATE_MILLIS);
} else {
cancel();
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
index d3c18e50..fdc35e98 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
@@ -6,13 +6,18 @@
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;
+import com.microsoft.copilot.eclipse.core.AuthStatusManager;
+import com.microsoft.copilot.eclipse.core.Constants;
import com.microsoft.copilot.eclipse.core.CopilotCore;
-import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
import com.microsoft.copilot.eclipse.core.lsp.protocol.SignInInitiateResult;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.dialogs.SignInConfirmDialog;
@@ -26,89 +31,115 @@
*/
public class SignInHandler extends AbstractHandler {
- private static final long SIGNIN_TIMEOUT_MILLIS = 180000L;
-
- private CopilotStatusManager copilotStatusManager;
+ private AuthStatusManager authStatusManager;
/**
* Initialize the Copilot Language Server for the SignInHandler.
*/
public SignInHandler() {
- this.copilotStatusManager = CopilotCore.getPlugin().getCopilotStatusManager();
+ this.authStatusManager = CopilotCore.getPlugin().getAuthStatusManager();
}
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
- Shell shell = SwtUtils.getShellFromEvent(event);
+ SignInJob signInJob = new SignInJob(event);
+ signInJob.schedule();
+
+ return null;
+ }
+
+ private class SignInJob extends Job {
+
+ private static final long SIGNIN_TIMEOUT_MILLIS = 180000L;
+
+ private final ExecutionEvent event;
- try {
+ /**
+ * Creates a new completion job.
+ */
+ public SignInJob(ExecutionEvent event) {
+ super("Initializing GitHub Copilot sign-in process...");
+ this.event = event;
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ Shell shell = SwtUtils.getShellFromEvent(event);
+ IStatus status = runInitiateSignIn(shell);
+ return status;
+ } catch (Exception e) {
+ String msg = Messages.signInHandler_msgDialog_signInFailed;
+ if (StringUtils.isNotBlank(e.getMessage())) {
+ msg += " " + e.getMessage();
+ CopilotUi.LOGGER.log(LogLevel.ERROR, msg, e);
+ }
+
+ String errorMsg = "Sign in failed: " + e.getMessage();
+ return new Status(IStatus.ERROR, Constants.PLUGIN_ID, errorMsg);
+ }
+ }
+
+ private IStatus runInitiateSignIn(Shell shell)
+ throws InterruptedException, java.util.concurrent.ExecutionException {
SignInInitiateResult result = initiateSignIn();
if (result.isAlreadySignedIn()) {
showAlreadySignedInMessage(shell);
+ return Status.OK_STATUS;
} else {
handleSignIn(shell, result);
+ return Status.OK_STATUS;
}
- } catch (Exception e) {
- handleSignInException(shell, e);
}
- return null;
- }
+ private SignInInitiateResult initiateSignIn() throws InterruptedException, java.util.concurrent.ExecutionException {
+ return authStatusManager.signInInitiate();
+ }
- private SignInInitiateResult initiateSignIn() throws Exception {
- return this.copilotStatusManager.signInInitiate();
- }
+ private void showAlreadySignedInMessage(Shell shell) {
+ MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_title,
+ Messages.signInHandler_msgDialog_alreadySignedIn);
+ }
- private void showAlreadySignedInMessage(Shell shell) {
- MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_title,
- Messages.signInHandler_msgDialog_alreadySignedIn);
- }
+ private void handleSignIn(Shell shell, SignInInitiateResult result) {
+ AtomicReference signInInitiateResultHolder = new AtomicReference<>(result);
+ SwtUtils.invokeOnDisplayThread(() -> {
+ SignInDialog signInDialog = new SignInDialog(shell, signInInitiateResultHolder.get());
+ int openResult = signInDialog.open();
+ if (openResult > 0) {
+ UiUtils.openLink(signInInitiateResultHolder.get().getVerificationUri());
+ SignInConfirmDialog signInConfirmDialog = new SignInConfirmDialog(shell,
+ signInInitiateResultHolder.get().getUserCode(), SIGNIN_TIMEOUT_MILLIS);
+ signInConfirmDialog.run();
+ handleSignInConfirmation(shell, signInConfirmDialog);
+ }
+ });
+ }
- private void handleSignIn(Shell shell, SignInInitiateResult result) {
- AtomicReference signInInitiateResultHolder = new AtomicReference<>(result);
- SwtUtils.invokeOnDisplayThread(() -> {
- SignInDialog signInDialog = new SignInDialog(shell, signInInitiateResultHolder.get());
- int btnId = signInDialog.open();
- if (btnId > 0) {
- UiUtils.openLink(signInInitiateResultHolder.get().getVerificationUri());
- SignInConfirmDialog signInConfirmDialog = new SignInConfirmDialog(shell,
- signInInitiateResultHolder.get().getUserCode(), SIGNIN_TIMEOUT_MILLIS);
- signInConfirmDialog.run();
- handleSignInConfirmation(shell, signInConfirmDialog);
+ private void handleSignInConfirmation(Shell shell, SignInConfirmDialog signInConfirmDialog) {
+ IStatus status = signInConfirmDialog.getStatus();
+ if (status != null && status.isOK()) {
+ showSignInSuccessMessage(shell);
+ authStatusManager.setCopilotStatus(CopilotStatusResult.OK);
+ } else {
+ showSignInFailMessage(shell, status);
+ authStatusManager.setCopilotStatus(CopilotStatusResult.NOT_SIGNED_IN);
}
- });
- }
-
- private void handleSignInConfirmation(Shell shell, SignInConfirmDialog signInConfirmDialog) {
- IStatus status = signInConfirmDialog.getStatus();
- if (status != null && status.isOK()) {
- showSignInSuccessMessage(shell);
- } else {
- showSignInFailMessage(shell, status);
}
- }
-
- private void showSignInSuccessMessage(Shell shell) {
- MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_githubCopilot,
- Messages.signInHandler_msgDialog_signInSuccess);
- }
- private void showSignInFailMessage(Shell shell, IStatus status) {
- String msg = Messages.signInHandler_msgDialog_signInFailed;
- if (status != null && StringUtils.isNotBlank(status.getMessage())) {
- msg += ": " + status.getMessage();
+ private void showSignInSuccessMessage(Shell shell) {
+ MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_githubCopilot,
+ Messages.signInHandler_msgDialog_signInSuccess);
}
- msg += ". ";
- MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_githubCopilot,
- msg + Messages.signInHandler_msgDialog_signInFailedTryAgain);
- }
- private void handleSignInException(Shell shell, Exception e) {
- String msg = Messages.signInHandler_msgDialog_signInFailed;
- if (StringUtils.isNotBlank(e.getMessage())) {
- msg += " " + e.getMessage();
- CopilotUi.LOGGER.log(LogLevel.ERROR, msg, e);
+ private void showSignInFailMessage(Shell shell, IStatus status) {
+ String msg = Messages.signInHandler_msgDialog_signInFailed;
+ if (status != null && StringUtils.isNotBlank(status.getMessage())) {
+ msg += ": " + status.getMessage();
+ }
+ msg += ". ";
+ MessageDialog.openInformation(shell, Messages.signInHandler_msgDialog_githubCopilot,
+ msg + Messages.signInHandler_msgDialog_signInFailedTryAgain);
}
- MessageDialog.openError(shell, Messages.signInHandler_msgDialog_signInFailedFailure, msg);
}
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
index 54f0b5b4..2c446363 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
@@ -7,8 +7,8 @@
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;
+import com.microsoft.copilot.eclipse.core.AuthStatusManager;
import com.microsoft.copilot.eclipse.core.CopilotCore;
-import com.microsoft.copilot.eclipse.core.CopilotStatusManager;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
@@ -20,23 +20,23 @@
*/
public class SignOutHandler extends AbstractHandler {
- private CopilotStatusManager copilotStatusManager;
+ private AuthStatusManager authStatusManager;
/**
* Initialize the Copilot Language Server and Auth Status Manager for the SignOutHandler.
*/
public SignOutHandler() {
- this.copilotStatusManager = CopilotCore.getPlugin().getCopilotStatusManager();
+ this.authStatusManager = CopilotCore.getPlugin().getAuthStatusManager();
}
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
Shell shell = SwtUtils.getShellFromEvent(event);
try {
- CopilotStatusResult result = copilotStatusManager.signOut();
+ CopilotStatusResult result = authStatusManager.signOut();
if (!result.isSignedIn()) {
showSignOutMessage(shell);
- copilotStatusManager.checkStatus();
+ authStatusManager.checkStatus();
}
} catch (Exception e) {
handleSignOutException(shell, e);
From 1b07a1efb54d43bc50c034863e3196d5fd00d813 Mon Sep 17 00:00:00 2001
From: yanshudan <1397370237@qq.com>
Date: Thu, 2 Jan 2025 13:15:57 +0800
Subject: [PATCH 048/690] feat - add basic proxy and auth proxy (#47)
---
.../META-INF/MANIFEST.MF | 3 +-
.../LanguageServerSettingManagerTests.java | 95 ++++++
.../META-INF/MANIFEST.MF | 3 +-
.../copilot/eclipse/core/Constants.java | 1 +
.../copilot/eclipse/core/CopilotCore.java | 19 +-
.../lsp/CopilotLanguageServerConnection.java | 10 +-
.../lsp/LanguageServerSettingManager.java | 121 ++++++++
.../CopilotLanguageServerSettings.java | 284 ++++++++++++++++++
launch/plugin_debug_configuration.launch | 1 +
9 files changed, 532 insertions(+), 5 deletions(-)
create mode 100644 com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/lsp/LanguageServerSettingManagerTests.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LanguageServerSettingManager.java
create mode 100644 com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CopilotLanguageServerSettings.java
diff --git a/com.microsoft.copilot.eclipse.core.test/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.core.test/META-INF/MANIFEST.MF
index e5428be1..aa98b007 100644
--- a/com.microsoft.copilot.eclipse.core.test/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.core.test/META-INF/MANIFEST.MF
@@ -15,4 +15,5 @@ Require-Bundle: com.microsoft.copilot.eclipse.core;bundle-version="0.1.0",
org.mockito.junit-jupiter;bundle-version="5.14.2",
org.eclipse.lsp4j,
org.eclipse.core.jobs,
- org.eclipse.equinox.common
+ org.eclipse.equinox.common,
+ org.eclipse.core.net;bundle-version="1.5.500"
diff --git a/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/lsp/LanguageServerSettingManagerTests.java b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/lsp/LanguageServerSettingManagerTests.java
new file mode 100644
index 00000000..1cf97be7
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core.test/src/com/microsoft/copilot/eclipse/core/lsp/LanguageServerSettingManagerTests.java
@@ -0,0 +1,95 @@
+package com.microsoft.copilot.eclipse.core.lsp;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.eclipse.core.net.proxy.IProxyData;
+import org.eclipse.core.net.proxy.IProxyService;
+import org.eclipse.lsp4j.DidChangeConfigurationParams;
+import org.junit.jupiter.api.Test;
+
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotLanguageServerSettings;
+
+/**
+ * Tests for the LanguageServerSettingManager.
+ */
+public class LanguageServerSettingManagerTests {
+ @Test
+ void testNoProxy() {
+ // when no proxy is applicable
+ // arrange
+ IProxyService mockProxyService = mock(IProxyService.class);
+ CopilotLanguageServerConnection mockLsConnection = mock(CopilotLanguageServerConnection.class);
+ when(mockProxyService.select(any())).thenReturn(null);
+ var params = new DidChangeConfigurationParams();
+ params.setSettings(new CopilotLanguageServerSettings());
+
+ // act
+ LanguageServerSettingManager manager = new LanguageServerSettingManager(mockLsConnection, mockProxyService);
+ manager.updateProxySettings();
+ manager.syncConfiguration();
+
+ // assert
+ verify(mockLsConnection, times(1)).updateConfig(params);
+ }
+
+ @Test
+ void testBasicProxy() {
+ // basic proxy test
+ // arrange
+ IProxyService mockProxyService = mock(IProxyService.class);
+ IProxyData mockProxyData = mock(IProxyData.class);
+ when(mockProxyData.getHost()).thenReturn("localhost");
+ when(mockProxyData.getPort()).thenReturn(8080);
+ when(mockProxyData.getType()).thenReturn("HTTPS");
+ when(mockProxyData.isRequiresAuthentication()).thenReturn(false);
+ when(mockProxyService.select(any())).thenReturn(new IProxyData[] { mockProxyData });
+ when(mockProxyService.isProxiesEnabled()).thenReturn(true);
+ var params = new DidChangeConfigurationParams();
+ var settings = new CopilotLanguageServerSettings();
+ settings.getHttp().setProxy("HTTPS://localhost:8080");
+ params.setSettings(settings);
+ CopilotLanguageServerConnection mockLsConnection = mock(CopilotLanguageServerConnection.class);
+
+ // act
+ LanguageServerSettingManager manager = new LanguageServerSettingManager(mockLsConnection, mockProxyService);
+ manager.updateProxySettings();
+ manager.syncConfiguration();
+
+ // assert
+ verify(mockLsConnection, times(1)).updateConfig(params);
+ }
+
+ @Test
+ void testBasicAuthProxy() {
+ // basic auth proxy test
+ // arrange
+ IProxyService mockProxyService = mock(IProxyService.class);
+ IProxyData mockProxyData = mock(IProxyData.class);
+ when(mockProxyData.getHost()).thenReturn("localhost");
+ when(mockProxyData.getPort()).thenReturn(8080);
+ when(mockProxyData.getType()).thenReturn("HTTPS");
+ when(mockProxyData.isRequiresAuthentication()).thenReturn(true);
+ when(mockProxyData.getUserId()).thenReturn("user");
+ when(mockProxyData.getPassword()).thenReturn("password");
+ when(mockProxyService.select(any())).thenReturn(new IProxyData[] { mockProxyData });
+ when(mockProxyService.isProxiesEnabled()).thenReturn(true);
+ var params = new DidChangeConfigurationParams();
+ var settings = new CopilotLanguageServerSettings();
+ settings.getHttp().setProxy("HTTPS://user:password@localhost:8080");
+ params.setSettings(settings);
+ CopilotLanguageServerConnection mockLsConnection = mock(CopilotLanguageServerConnection.class);
+
+ // act
+ LanguageServerSettingManager manager = new LanguageServerSettingManager(mockLsConnection, mockProxyService);
+ manager.updateProxySettings();
+ manager.syncConfiguration();
+
+ // assert
+ verify(mockLsConnection, times(1)).updateConfig(params);
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
index d8fcecc4..d1622fae 100644
--- a/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
+++ b/com.microsoft.copilot.eclipse.core/META-INF/MANIFEST.MF
@@ -22,4 +22,5 @@ Require-Bundle: org.eclipse.lsp4e;bundle-version="0.18.12",
org.eclipse.jdt.annotation;bundle-version="2.3.0",
org.eclipse.jface.text,
com.google.gson;bundle-version="2.11.0",
- org.eclipse.wildwebdeveloper.embedder.node;bundle-version="1.0.3";resolution:=optional
+ org.eclipse.wildwebdeveloper.embedder.node;bundle-version="1.0.3";resolution:=optional,
+ org.eclipse.core.net;bundle-version="1.5.500"
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java
index 95d5479c..e0b7e0c7 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java
@@ -11,4 +11,5 @@ private Constants() {
public static final String PLUGIN_ID = "com.microsoft.copilot.eclipse";
public static final String AUTO_SHOW_COMPLETION = "enableAutoCompletions";
+ public static final String GITHUB_COPILOT_URL = "http://github.com";
}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
index cad2f071..2bf26a9f 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/CopilotCore.java
@@ -2,6 +2,7 @@
import java.util.Objects;
+import org.eclipse.core.net.proxy.IProxyService;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Plugin;
@@ -11,11 +12,15 @@
import org.eclipse.lsp4e.LanguageServersRegistry;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
import com.microsoft.copilot.eclipse.core.logger.CopilotForEclipseLogger;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
+import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServer;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
+import com.microsoft.copilot.eclipse.core.lsp.LanguageServerSettingManager;
/**
* The plug-in runtime class for the Copilot plug-in containing the core (UI-free) support, like the completion,
@@ -26,6 +31,7 @@ public class CopilotCore extends Plugin {
private CopilotLanguageServerConnection copilotLanguageServer;
private AuthStatusManager authStatusManager;
private CompletionProvider completionProvider;
+ private LanguageServerSettingManager languageServerSettingManager;
private static CopilotCore COPILOT_CORE_PLUGIN = null;
public static final CopilotForEclipseLogger LOGGER = new CopilotForEclipseLogger(CopilotCore.class.getName());
@@ -50,7 +56,7 @@ public static CopilotCore getPlugin() {
@Override
public void start(BundleContext context) throws Exception {
- init();
+ init(context);
}
@Override
@@ -58,10 +64,13 @@ public void stop(BundleContext context) throws Exception {
if (copilotLanguageServer != null) {
copilotLanguageServer.stop();
}
+ if (this.languageServerSettingManager != null) {
+ this.languageServerSettingManager.dispose();
+ }
}
@SuppressWarnings("restriction")
- void init() {
+ void init(BundleContext context) {
final Runnable initRunnable = () -> {
LanguageServersRegistry.LanguageServerDefinition serverDef = LanguageServersRegistry.getInstance()
.getDefinition(CopilotLanguageServerConnection.SERVER_ID);
@@ -78,6 +87,12 @@ void init() {
this.completionProvider = new CompletionProvider(this.copilotLanguageServer, authStatusManager);
this.authStatusManager.checkStatus();
+ // initialize the LanguageServerSettingManager
+ ServiceReference> serviceReference = context.getServiceReference(IProxyService.class.getName());
+ this.languageServerSettingManager = new LanguageServerSettingManager(this.copilotLanguageServer,
+ (IProxyService) context.getService(serviceReference));
+ this.languageServerSettingManager.updateProxySettings();
+ this.languageServerSettingManager.syncConfiguration();
};
Job initJob = new Job("GitHub Copilot Initialization...") {
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
index 2b7078d1..8e2b768f 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java
@@ -7,6 +7,7 @@
import org.eclipse.jface.text.IDocument;
import org.eclipse.lsp4e.LanguageServerWrapper;
+import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.services.LanguageServer;
import com.microsoft.copilot.eclipse.core.AuthStatusManager;
@@ -84,7 +85,14 @@ public CompletableFuture getCompletions(CompletionParams param
}
/**
- * Please use the {@link AuthStatusManager#signInInitiate()} method instead.
+ * Update the configuration for the language server.
+ */
+ public void updateConfig(DidChangeConfigurationParams params) {
+ this.languageServerWrapper.sendNotification(server -> server.getWorkspaceService().didChangeConfiguration(params));
+ }
+
+ /**
+ * Please use the {@link CopilotStatusManager#signInInitiate()} method instead.
*
* Initiate the sign in process.
*/
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LanguageServerSettingManager.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LanguageServerSettingManager.java
new file mode 100644
index 00000000..be99ef22
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LanguageServerSettingManager.java
@@ -0,0 +1,121 @@
+package com.microsoft.copilot.eclipse.core.lsp;
+
+import java.net.URI;
+
+import org.eclipse.core.net.proxy.IProxyChangeEvent;
+import org.eclipse.core.net.proxy.IProxyChangeListener;
+import org.eclipse.core.net.proxy.IProxyData;
+import org.eclipse.core.net.proxy.IProxyService;
+import org.eclipse.lsp4j.DidChangeConfigurationParams;
+
+import com.microsoft.copilot.eclipse.core.Constants;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
+import com.microsoft.copilot.eclipse.core.logger.LogLevel;
+import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotLanguageServerSettings;
+
+/**
+ * A class to manage the proxy service for the Copilot Language Server.
+ */
+public class LanguageServerSettingManager implements IProxyChangeListener {
+ IProxyService proxyService = null;
+ CopilotLanguageServerSettings settings = new CopilotLanguageServerSettings();
+ CopilotLanguageServerConnection copilotLanguageServerConnection = null;
+
+ /**
+ * Initializes the LanguageServerSettingManager.
+ */
+ public LanguageServerSettingManager(CopilotLanguageServerConnection conn, IProxyService proxyService) {
+ this.copilotLanguageServerConnection = conn;
+ this.proxyService = proxyService;
+
+ // add listners
+ proxyService.addProxyChangeListener(this);
+ }
+
+ /**
+ * A listener for the proxy service.
+ */
+ @Override
+ public void proxyInfoChanged(IProxyChangeEvent event) {
+ CopilotCore.LOGGER.log(LogLevel.INFO, "Proxy info changed");
+ updateProxySettings();
+ syncConfiguration();
+ }
+
+ /**
+ * Synchronizes the configuration with the language server.
+ */
+ public void syncConfiguration() {
+ DidChangeConfigurationParams params = new DidChangeConfigurationParams();
+ params.setSettings(settings);
+ this.copilotLanguageServerConnection.updateConfig(params);
+ }
+
+ /**
+ * Updates the proxy settings.
+ */
+ public void updateProxySettings() {
+ IProxyData proxyData = getProxy();
+ if (proxyData == null) {
+ settings.getHttp().setProxy(null);
+ CopilotCore.LOGGER.log(LogLevel.INFO, "No proxy data found");
+ return;
+ }
+ settings.getHttp().setProxy(createProxyString(proxyData));
+ CopilotCore.LOGGER.log(LogLevel.INFO, String.format("Proxy will be updated to %s", settings.getHttp().getProxy()));
+
+ }
+
+ /**
+ * Gets the proxy data.
+ *
+ * @return the proxy data
+ */
+ private IProxyData getProxy() {
+ if (proxyService == null) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, "Proxy service is null");
+ return null;
+ }
+ if (!proxyService.isProxiesEnabled()) {
+ CopilotCore.LOGGER.log(LogLevel.INFO, "Proxies are disabled");
+ return null;
+ }
+ IProxyData[] proxyData = proxyService.select(URI.create(Constants.GITHUB_COPILOT_URL));
+ if (proxyData != null && proxyData.length > 0) {
+ return proxyData[0];
+ }
+ return null;
+ }
+
+ /**
+ * Creates a proxy string from the given proxy data.
+ *
+ * @param proxyData the proxy data
+ * @return the proxy string
+ */
+ public static String createProxyString(IProxyData proxyData) {
+ if (proxyData == null) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, "Proxy data is null");
+ return null;
+ }
+
+ String proxyString = proxyData.getType() + "://";
+ String host = proxyData.getHost();
+ int port = proxyData.getPort();
+ String user = proxyData.getUserId();
+ String password = proxyData.getPassword();
+
+ if (proxyData.isRequiresAuthentication()) {
+ proxyString += user + ":" + password + "@";
+ }
+ proxyString += host + ":" + port;
+ return proxyString;
+ }
+
+ /**
+ * Disposes the resources of this LanguageServerSettingManager.
+ */
+ public void dispose() {
+ proxyService.removeProxyChangeListener(this);
+ }
+}
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CopilotLanguageServerSettings.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CopilotLanguageServerSettings.java
new file mode 100644
index 00000000..2e84f54c
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/CopilotLanguageServerSettings.java
@@ -0,0 +1,284 @@
+package com.microsoft.copilot.eclipse.core.lsp.protocol;
+
+import java.util.Objects;
+
+import com.google.gson.annotations.SerializedName;
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
+
+/**
+ * Settings for the DidChangeConfigurationParams.
+ */
+public class CopilotLanguageServerSettings {
+ private boolean showEditorCompletions;
+ private boolean enableAutoCompletions;
+
+ /**
+ * Http settings.
+ */
+ public class Http {
+
+ private String proxy;
+ @SerializedName("proxyStrictSSL")
+ private boolean proxyStrictSsl;
+ private String proxyKerberosServicePrincipal;
+
+
+ /**
+ * get proxy.
+ *
+ * @return the proxy
+ */
+ public String getProxy() {
+ return proxy;
+ }
+
+ /**
+ * set proxy.
+ *
+ * @param proxy the proxy to set
+ */
+ public void setProxy(String proxy) {
+ this.proxy = proxy;
+ }
+
+ /**
+ * is proxy strict ssl.
+ *
+ * @return the proxyStrictSsl
+ */
+ public boolean isProxyStrictSsl() {
+ return proxyStrictSsl;
+ }
+
+ /**
+ * set proxy strict ssl.
+ *
+ * @param proxyStrictSsl the proxyStrictSsl to set
+ */
+ public void setProxyStrictSsl(boolean proxyStrictSsl) {
+ this.proxyStrictSsl = proxyStrictSsl;
+ }
+
+ /**
+ * get proxy kerberos service principal.
+ *
+ * @return the proxyKerberosServicePrincipal
+ */
+ public String getProxyKerberosServicePrincipal() {
+ return proxyKerberosServicePrincipal;
+ }
+
+ /**
+ * set proxy kerberos service principal.
+ *
+ * @param proxyKerberosServicePrincipal the proxyKerberosServicePrincipal to set
+ */
+ public void setProxyKerberosServicePrincipal(String proxyKerberosServicePrincipal) {
+ this.proxyKerberosServicePrincipal = proxyKerberosServicePrincipal;
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("proxy", proxy);
+ builder.add("proxyStrictSsl", proxyStrictSsl);
+ builder.add("proxyKerberosServicePrincipal", proxyKerberosServicePrincipal);
+ return builder.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(proxy, proxyKerberosServicePrincipal, proxyStrictSsl);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ Http other = (Http) obj;
+ return Objects.equals(proxy, other.proxy)
+ && Objects.equals(proxyKerberosServicePrincipal, other.proxyKerberosServicePrincipal)
+ && proxyStrictSsl == other.proxyStrictSsl;
+ }
+
+ }
+
+ /**
+ * Github Enterprise settings.
+ */
+ public class GithubEnterprise {
+ private String uri = "http://github.com";
+
+ /**
+ * get Uri.
+ *
+ * @return the uri
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ /**
+ * set Uri.
+ *
+ * @param uri the uri to set
+ */
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("uri", uri);
+ return builder.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uri);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ GithubEnterprise other = (GithubEnterprise) obj;
+ return Objects.equals(uri, other.uri);
+ }
+
+ }
+
+ @SerializedName("github-enterprise")
+ private GithubEnterprise githubEnterprise;
+ private Http http;
+
+ /**
+ * Constructor.
+ */
+ public CopilotLanguageServerSettings() {
+ this.showEditorCompletions = true;
+ this.enableAutoCompletions = true;
+ this.http = new Http();
+ this.githubEnterprise = new GithubEnterprise();
+ }
+
+ /**
+ * is show editor completions.
+ *
+ * @return the showEditorCompletions
+ */
+ public boolean isShowEditorCompletions() {
+ return showEditorCompletions;
+ }
+
+ /**
+ * set show editor completions.
+ *
+ * @param showEditorCompletions the showEditorCompletions to set
+ */
+ public void setShowEditorCompletions(boolean showEditorCompletions) {
+ this.showEditorCompletions = showEditorCompletions;
+ }
+
+ /**
+ * is enable auto completions.
+ *
+ * @return the enableAutoCompletions
+ */
+ public boolean isEnableAutoCompletions() {
+ return enableAutoCompletions;
+ }
+
+ /**
+ * set enable auto completions.
+ *
+ * @param enableAutoCompletions the enableAutoCompletions to set
+ */
+ public void setEnableAutoCompletions(boolean enableAutoCompletions) {
+ this.enableAutoCompletions = enableAutoCompletions;
+ }
+
+ /**
+ * get github enterprise.
+ *
+ * @return the githubEnterprise
+ */
+ public GithubEnterprise getGithubEnterprise() {
+ return githubEnterprise;
+ }
+
+ /**
+ * set github enterprise.
+ *
+ * @param githubEnterprise the githubEnterprise to set
+ */
+ public void setGithubEnterprise(GithubEnterprise githubEnterprise) {
+ this.githubEnterprise = githubEnterprise;
+ }
+
+ /**
+ * get http.
+ *
+ * @return the http
+ */
+ public Http getHttp() {
+ return http;
+ }
+
+ /**
+ * set http.
+ *
+ * @param http the http to set
+ */
+ public void setHttp(Http http) {
+ this.http = http;
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ builder.add("showEditorCompletions", showEditorCompletions);
+ builder.add("enableAutoCompletions", enableAutoCompletions);
+ builder.add("githubEnterprise", githubEnterprise);
+ builder.add("http", http);
+ return builder.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(enableAutoCompletions, githubEnterprise, http, showEditorCompletions);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ CopilotLanguageServerSettings other = (CopilotLanguageServerSettings) obj;
+ return enableAutoCompletions == other.enableAutoCompletions
+ && Objects.equals(githubEnterprise, other.githubEnterprise) && Objects.equals(http, other.http)
+ && showEditorCompletions == other.showEditorCompletions;
+ }
+
+}
diff --git a/launch/plugin_debug_configuration.launch b/launch/plugin_debug_configuration.launch
index f937ebb3..0eb4d706 100644
--- a/launch/plugin_debug_configuration.launch
+++ b/launch/plugin_debug_configuration.launch
@@ -84,6 +84,7 @@
+
From 5a3e7e7e45ad8d35d4b13aa6fec7d4cbbffdb550 Mon Sep 17 00:00:00 2001
From: ethanyhou <149548697+ethanyhou@users.noreply.github.com>
Date: Fri, 3 Jan 2025 16:12:44 +0800
Subject: [PATCH 049/690] [feat] - Enabled key bindings on the preferences
settings, updated context menu, and updated project name to GitHub Copilot.
(#87)
---
com.microsoft.copilot.eclipse.core/plugin.xml | 2 +-
.../core/lsp/LsStreamConnectionProvider.java | 2 +-
.../feature.xml | 2 +-
.../category.xml | 2 +-
.../icons/edit_keyboard_shortcuts.png | Bin 0 -> 604 bytes
.../icons/edit_keyboard_shortcuts@2x.png | Bin 0 -> 1266 bytes
.../icons/edit_preferences.png | Bin 0 -> 732 bytes
.../icons/edit_preferences@2x.png | Bin 0 -> 1512 bytes
.../plugin.properties | 7 ++--
com.microsoft.copilot.eclipse.ui/plugin.xml | 31 +++++++++++++++++-
.../OpenEditKeyboardShortcutsHandler.java | 31 ++++++++++++++++++
.../ui/handlers/OpenPreferencesHandler.java | 28 ++++++++++++++++
.../ui/handlers/ShowStatusBarMenuHandler.java | 21 ++++++++++--
.../copilot/eclipse/ui/i18n/Messages.java | 2 ++
.../eclipse/ui/i18n/messages.properties | 4 ++-
pom.xml | 2 +-
16 files changed, 123 insertions(+), 11 deletions(-)
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/edit_keyboard_shortcuts.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/edit_keyboard_shortcuts@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/edit_preferences.png
create mode 100644 com.microsoft.copilot.eclipse.ui/icons/edit_preferences@2x.png
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/OpenEditKeyboardShortcutsHandler.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/OpenPreferencesHandler.java
diff --git a/com.microsoft.copilot.eclipse.core/plugin.xml b/com.microsoft.copilot.eclipse.core/plugin.xml
index a55a9fda..8954f14a 100644
--- a/com.microsoft.copilot.eclipse.core/plugin.xml
+++ b/com.microsoft.copilot.eclipse.core/plugin.xml
@@ -12,7 +12,7 @@
point="org.eclipse.lsp4e.languageServer">
-
+
\ No newline at end of file
diff --git a/com.microsoft.copilot.eclipse.ui/icons/edit_keyboard_shortcuts.png b/com.microsoft.copilot.eclipse.ui/icons/edit_keyboard_shortcuts.png
new file mode 100644
index 0000000000000000000000000000000000000000..70a850f05c4e070e8d0bc455ed0cd2796918dc3e
GIT binary patch
literal 604
zcmV-i0;BzjP)ypzjN++{Lb&3101xFdam4iGhZleCW0W~
zZJ!ivO94x(1X}2xOq+>nMZ(zv~&1d0>@)NE8j-=HwUt_35>NrE&)t_MmMaStgbx
zU%lN#n!&M{Q$`JC7iItWKdEf`{C8aYKKvZDF;ZH8K`Cb#yzTB2?Jpmb8kVX^mdjaq
zhfR4cDnUIa2ldw*NiVU+iBQOPSD}#khN9g$-ET?3^Rnb+bMXDm^7U{y0doa8LKvz}
ztOLD6Y3w>D+*lOp!7l8T(+$rJK#d!PuszlJo<6vyhGG54h0M;)VQFO@Ey-_KTwRBk
z$z$kGhXyeKvFJj2dNz?SmZ(tjAvFp`p9)gvi#}pb6m@57(cbzHFOuJNVcnTpR4lJj
zy}Y=*#*I(jbN}!JU%1hxnDVLmXQH|?2*RV*E_5cFapz_OE?qpQy^*6wh$<<+)s*6R
q+{MWg$FY&g%8TT*-taPv{mK?O==G6UMXtF30000Q6lD5$LgT)6?s5Cy6V0|cJpbaESpft2Kg_8c-AKDi~
zTViPi1@j^Zf<8qwA`u@1K@dezM11fM$-TRKjx%$9_UvAxBKJ)PmYp*@=ggdMzB6Y5
zp30Lh=#)3sZyjo;+4eNc7Avhm(}Rfq&7yhGI+tz8-=Zi&>*LoA<~ox`Sr{(R*Rj}H
zyI)&A6CplBYlt)ZR~NIodvu@!nn*fKt}0~L8b7YS9;cc%Fc^?0-Z@6(VdhFBobQHLmswNeW0<^_r0|2rZp4{cl~_Fw*;mOXCUv{Dux-+4&pj}EMs
zP+$FaK*rw=uR{*@<*x_Oj(u`Gjv=S72q6LxM>#5O_QBZ(&;r{-OqI4s_SKXv@OHr4r;?2OOx;DM06B69BKf
z#hky;Nd;qN0{O8vJb@s%1~)bc6m5#+^B;HVEX(1G!Hp0xq+rjzL2x82l37S4w(ZOWvg1RcbI
zEJNQst)e^x24>qxgb!v_Ar*f3$rv=6Dakz(
zltRIzUYF#LO??NCA}{uCl6T}0p#&HO>N~&>~msv)fJtF~c-nk1mDX0rPvZW732KszC
zS`tvo5ysv|?qwK((g+O?j8*tZ2v7-J0v~;_0`~5ohSO&+wxH0x2UBSfAW6T=JKHNobR--~596K?u0qM8?4^2`dlLwy=@4*-t#hCzIqLozw;*4>C|vgZSDs7(UqRA?8E;G
c#tu*A9#%Zn@b=Iz761SM07*qoM6N<$f*;REwg3PC
literal 0
HcmV?d00001
diff --git a/com.microsoft.copilot.eclipse.ui/icons/edit_preferences.png b/com.microsoft.copilot.eclipse.ui/icons/edit_preferences.png
new file mode 100644
index 0000000000000000000000000000000000000000..2c6abc592ea45390a1fcaf4d2d2a2b003540b475
GIT binary patch
literal 732
zcmV<20wev2P)?5xNSR2uT|uqA23lU~MbZCSDZ$SwRGA!PKjV)`Q|P-U_|co3|_&rBoqR
zL`)ExOIs<5D}ohN3`wd<)Mhm%o84{p`QDm@1}Pq-1Ixa*^L;b(&CCM)r=j&6*G4YR
z0^q^=_2ES1p775AH$ow=!RVvy279xN^jK*0*dQPT;^ecW1G8r(y~lOf@*n
zT5=6(vt7RJV`cItpE%QjG0no2O`lVs7>aH1ER93o!Mzc$Z(HxBD@O?dyD)k9L!H2J
zcF3f@xw;Q~UBXZgPJJkBDHcm;UV8JiB|y&sf4o%ubsQPGYar+Y-tPvxmBALKhIqdR
zx^{Aqj4z?rFVd0&S4NW3yJ}Oy^~*ysmS`sR!l(BIz`BCvLJ_9LMRe!(0?7Flz^Vc%
z;-hUVBS-8{lL0o>WeUT?d(299xHe;~R?wpdZ^5(6ZxRxZB{+9}H$HXJi)6XVQ(QE<
z1kC`Zt?DE{K>24GvA{x`6*Y%ct!QAV3Tk6&1h)!ntsVd+qf;O0f$MbJ`3r&vf=9OE
zCy51QvoUZY=mbMi;QmYo(pg0!IGMzjTlOFv3Ynwo@9yCH{T!VHDF`826+gN;D;
zt3E^K9H2VMG66OWs#Ml78z5EPq8N&8@YesC$Zin6&}i~RP)=ie0ttz{*<=J*3k6bfK84-ywRu4+4t?VHhm!ft
zZ7+dmZ52n2)YILMhrc?Xvaj9e^I*}Wd++Cm4(GC;GXuM}w|*3h>76m0>S_12dQzoi
z{1TzLPIe9$?Iaz)6C3bv0LVrn*b{#(<8J9p0l|?!*2XmF464ik2zzpYu;g=Yq|q3kheoZ;
zt`u4A8W&W|FJiAAhm{M4-v!Sa2T6NxZdnCZYcbpz8U^pBrQoiv0E%fOnW$Oz6l{HeJ)Jrc
z#PYc%(Dcq~By9?$PN8de7_`uIe;Mb1sx_iYW$kJQG;di&r0ba2LCUuIDh0q~
zgJyFa{U!i`OM}WeYc=lk9}Gag5(u3OTpe!ktmt4TEqCvXS;rnkux4omOLUG{N77?}
zy3R}Wa62*vilLOlcnCU<^};XR138_il%Rb_+fLq`dejJHlXv7ngzZPo02K|~8iBsw
z!YW)Y0Xi3#lZsv)OOSpsjfA+>ck%>HwtRZufrI`O0HEs`MyQd@^&<`3vmsWMMppuXUJ#S`Znn_f%o`|;>;=nIC?xyVL3mqCYJJlM&V@F$
z+wt@)YN)HgvlqfD?ItzU&4Wb?tVEh-^vcj*@a>TS$VZO>5NzY)vLQukbg!qY`yRAx
zUSx8y{)KXIESN#pZ${L6b=7Q;+EbL%AJ0@*cQ_s{p5pjJDQ0!LoG=DiDEU@fz1$8j
z*FTvgO`dnDr0!<^Io=t@-fQE+;z0yi2T@Tz=gX=~;FX4QlIh2T5J8U-#B^io$y)J9
zH6X+%qx`LQ&cd-jV-S8AwKAXV>-DZgcGDh<4I}N@Mh?W&ZS`%uZxiS7R;h6tKqN_VD
zj6?9|gwnY^JPA$f?CfBM8V@&qpM+ySKT61W_`s&MrK(Vlied=%D^VGO#m#>MIWr*l
zb-ibWoLFx^8@6-v_w@Y@!CRA{E*8LUUbtPAMKrI}qDnCwjza$*3Md;#t3>>0T!V=z
z9Zp_QeIR4x<9+_G_fPki@r&qF|G_=7LDnZPMj#lP0`pem+^VxhF+%Ih>FI
zu#V^kZ%wiDNW!_;%;YQUH(@e}Ojm0B_z+Ax4oW8uBA@e|089`%S>DKTp9>D0KH)^S
zh+->=ERDIGn+6J^UE8+!x$(AUvF7J{ej5}(1^;KH69lpg;WYeD1=2rdw7K|4<
+
+
+
+
+
+
@@ -102,6 +123,14 @@
class="com.microsoft.copilot.eclipse.ui.handlers.ViewFeedbackForumHandler"
commandId="com.microsoft.copilot.eclipse.commands.viewFeedbackForum">
+
+
+
+
@@ -126,4 +155,4 @@
sequence="ESC">
-
\ No newline at end of file
+
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/OpenEditKeyboardShortcutsHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/OpenEditKeyboardShortcutsHandler.java
new file mode 100644
index 00000000..0b2db97b
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/OpenEditKeyboardShortcutsHandler.java
@@ -0,0 +1,31 @@
+package com.microsoft.copilot.eclipse.ui.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.preference.IPreferenceNode;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.preference.PreferenceManager;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+
+import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
+
+/**
+ * Handler for opening the preferences dialog.
+ */
+public class OpenEditKeyboardShortcutsHandler extends AbstractHandler {
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ Shell shell = SwtUtils.getShellFromEvent(event);
+ PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(shell,
+ "org.eclipse.ui.preferencePages.Keys",
+ new String[] { "org.eclipse.ui.preferencePages.Keys" }, null);
+ dialog.open();
+
+ return null;
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/OpenPreferencesHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/OpenPreferencesHandler.java
new file mode 100644
index 00000000..5a1f01a4
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/OpenPreferencesHandler.java
@@ -0,0 +1,28 @@
+package com.microsoft.copilot.eclipse.ui.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+
+import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
+
+/**
+ * Handler for opening the preferences dialog.
+ */
+public class OpenPreferencesHandler extends AbstractHandler {
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ Shell shell = SwtUtils.getShellFromEvent(event);
+ PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(shell,
+ "com.microsoft.copilot.eclipse.ui.preferences.page",
+ new String[] { "com.microsoft.copilot.eclipse.ui.preferences.page" }, null);
+ dialog.open();
+
+ return null;
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
index 43e0dcf6..ceaa4a5d 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
@@ -58,6 +58,11 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
addSignInOrSignOutAction(menuManager);
}
+ // Preferences section
+ menuManager.add(new Separator());
+ addEditKeyboardShortcutsAction(menuManager);
+ addPreferencesAction(menuManager);
+
Shell shell = PlatformUI.getWorkbench().getDisplay().getActiveShell();
Menu menu = menuManager.createContextMenu(shell);
menu.setVisible(true);
@@ -115,6 +120,19 @@ private void addLinkToFeedbackForumAction(MenuManager menuManager) {
"com.microsoft.copilot.eclipse.commands.viewFeedbackForum", true);
}
+ private void addPreferencesAction(MenuManager menuManager) {
+ ImageDescriptor editPreferencesIcon = UiUtils.buildImageDescriptorFromPngPath("/icons/edit_preferences.png");
+ MenuActionFactory.createMenuAction(menuManager, Messages.menu_editPreferences, editPreferencesIcon, handlerService,
+ "com.microsoft.copilot.eclipse.commands.openPreferences", true);
+ }
+
+ private void addEditKeyboardShortcutsAction(MenuManager menuManager) {
+ ImageDescriptor editKeyboardShortcutsIcon = UiUtils
+ .buildImageDescriptorFromPngPath("/icons/edit_keyboard_shortcuts.png");
+ MenuActionFactory.createMenuAction(menuManager, Messages.menu_editKeyboardShortcuts, editKeyboardShortcutsIcon,
+ handlerService, "com.microsoft.copilot.eclipse.commands.openEditKeyboardShortcuts", true);
+ }
+
private String getCopilotStatusBasedOnAuthAndCompletionResult(String copilotStatus) {
CopilotStatusManager completionStatusManager = getAuthAndCompletionStatusManager();
switch (copilotStatus) {
@@ -185,7 +203,6 @@ private class SpinnerJob extends Job {
private static final int INITIAL_ICON_INDEX = 1;
private static final int TOTAL_SPINNER_ICONS = 8;
private static final long COMPLETION_IN_PROGRESS_SPINNER_ROTATE_RATE_MILLIS = 200L;
-
private int currentIconIndex = INITIAL_ICON_INDEX;
private UIElement uiElement;
@@ -194,7 +211,7 @@ public SpinnerJob() {
super("Spinner Job");
this.setSystem(true);
}
-
+
public void setTargetUiElement(UIElement uiElement) {
this.uiElement = uiElement;
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
index 62924551..85a17a9a 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/Messages.java
@@ -18,6 +18,8 @@ public final class Messages extends NLS {
public static String menu_signToGitHub;
public static String menu_signOutFromGitHub;
public static String menu_viewFeedbackForum;
+ public static String menu_editPreferences;
+ public static String menu_editKeyboardShortcuts;
public static String signInDialog_title;
public static String signInDialog_button_cancel;
public static String signInDialog_button_copyOpen;
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
index 99559d83..8eb4c8e7 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/i18n/messages.properties
@@ -9,6 +9,8 @@ menu_copilotStatus_agentWarning=Copilot is encountering temporary issues
menu_signToGitHub=Sign In to GitHub
menu_signOutFromGitHub=Sign Out from GitHub
menu_viewFeedbackForum=View Feedback Forum...
+menu_editPreferences=Edit Preferences...
+menu_editKeyboardShortcuts=Edit Keyboard Shortcuts...
signInDialog_title=Sign In to GitHub
signInDialog_button_cancel=Cancel
@@ -33,5 +35,5 @@ signOutHandler_msgDialog_signOutSuccess=You have successfully signed out from Co
signOutHandler_msgDialog_signOutFailed=Unable to sign out to GitHub Copilot at this time
signOutHandler_msgDialog_signOutFailedFailure=Copilot Sign Out Failure
-preferencesPage_description=Configure GitHub Copilot for Eclipse
+preferencesPage_description=Configure GitHub Copilot
preferencesPage_autoShowCompletion=Automatically show inline completions
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index aa586a87..066952f4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
${base.name}
- GitHub Copilot for Eclipse
+ GitHub Copilot
4.0.10
3.6.0
From 1402f145a74d1ca5dc2553c9140b987f0e05edca Mon Sep 17 00:00:00 2001
From: yanshudan <1397370237@qq.com>
Date: Mon, 6 Jan 2025 08:57:41 +0800
Subject: [PATCH 050/690] fix - Remove duplicated loggers (#88)
---
.../core/logger/CopilotForEclipseLogger.java | 2 +-
.../com/microsoft/copilot/eclipse/ui/CopilotUi.java | 5 ++---
.../eclipse/ui/completion/CompletionHandler.java | 13 +++++++------
.../eclipse/ui/completion/CompletionManager.java | 5 +++--
.../eclipse/ui/dialogs/SignInConfirmDialog.java | 4 ++--
.../ui/handlers/ShowStatusBarMenuHandler.java | 2 +-
.../copilot/eclipse/ui/handlers/SignInHandler.java | 4 ++--
.../copilot/eclipse/ui/handlers/SignOutHandler.java | 2 +-
.../ui/handlers/ViewFeedbackForumHandler.java | 3 ++-
.../microsoft/copilot/eclipse/ui/utils/UiUtils.java | 3 ++-
10 files changed, 23 insertions(+), 20 deletions(-)
diff --git a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/CopilotForEclipseLogger.java b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/CopilotForEclipseLogger.java
index 44ef6738..fb8bfbaa 100644
--- a/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/CopilotForEclipseLogger.java
+++ b/com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/logger/CopilotForEclipseLogger.java
@@ -14,7 +14,7 @@
* The logger for Copilot for Eclipse.
*/
public class CopilotForEclipseLogger {
- //TODO: migrate to xml configuration
+ // TODO: migrate to xml configuration
private Logger logger;
/**
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
index 84dbfd49..bcaa6aa0 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/CopilotUi.java
@@ -29,7 +29,6 @@ public class CopilotUi extends AbstractUIPlugin {
private CopilotStatusManager copilotStatusManager;
private EditorLifecycleListener editorLifecycleListener;
private EditorsManager editorsManager;
- public static final CopilotForEclipseLogger LOGGER = new CopilotForEclipseLogger(CopilotCore.class.getName());
/**
* Creates the Copilot ui plugin. The plugin is created automatically by the Eclipse framework. Clients must not call
@@ -56,7 +55,7 @@ protected IStatus run(IProgressMonitor monitor) {
CopilotLanguageServerConnection connection = CopilotCore.getPlugin().getCopilotLanguageServer();
if (connection == null) {
var ex = new IllegalStateException("Failed to start copilot language server.");
- LOGGER.log(LogLevel.ERROR, ex);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, ex);
throw ex;
}
@@ -73,7 +72,7 @@ protected IStatus run(IProgressMonitor monitor) {
// to initialize it.
initCompletionHandlerForActiveEditor();
} catch (OperationCanceledException | InterruptedException e) {
- LOGGER.log(LogLevel.ERROR, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
return Status.error("Failed to initialize GitHub Copilot plugin.", e);
}
return Status.OK_STATUS;
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
index e2bfc4e8..e7056b38 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
@@ -19,6 +19,7 @@
import org.eclipse.ui.texteditor.ITextEditor;
import com.microsoft.copilot.eclipse.core.Constants;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
@@ -56,23 +57,23 @@ public CompletionHandler(CopilotLanguageServerConnection lsConnection, Completio
// if the text viewer is null, we will not register listeners.
// the side effect is that the completion will not be triggered for this editor.
if (textViewer == null) {
- CopilotUi.LOGGER.log(LogLevel.INFO, "Text viewer is null for editor: " + editor.getTitle());
+ CopilotCore.LOGGER.log(LogLevel.INFO, "Text viewer is null for editor: " + editor.getTitle());
return;
}
this.document = LSPEclipseUtils.getDocument(editor);
if (this.document == null) {
- CopilotUi.LOGGER.log(LogLevel.INFO, "Document is null for editor: " + editor.getTitle());
+ CopilotCore.LOGGER.log(LogLevel.INFO, "Document is null for editor: " + editor.getTitle());
return;
}
this.documentUri = LSPEclipseUtils.toUri(document);
if (this.documentUri == null) {
- CopilotUi.LOGGER.log(LogLevel.INFO, "Document URI is null for editor: " + editor.getTitle());
+ CopilotCore.LOGGER.log(LogLevel.INFO, "Document URI is null for editor: " + editor.getTitle());
return;
}
try {
lsConnection.connectDocument(this.document);
} catch (IOException e) {
- CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
return;
}
this.documentVersion = -1;
@@ -111,7 +112,7 @@ public void acceptFullSuggestion() {
this.completionManager.acceptSuggestion();
this.document.removePosition(this.triggerPosition);
} catch (BadLocationException e) {
- CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
return;
}
this.clearCompletionRendering();
@@ -202,7 +203,7 @@ public void dispose() {
try {
this.document.removePositionCategory(this.getCategory());
} catch (BadPositionCategoryException e) {
- CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
}
this.document.removePositionUpdater(this.positionUpdater);
SwtUtils.invokeOnDisplayThread(() -> {
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
index 5c76d34d..b38235c1 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
@@ -18,6 +18,7 @@
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
import com.microsoft.copilot.eclipse.core.completion.CompletionListener;
import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
@@ -78,7 +79,7 @@ public void triggerCompletion(Position position, int documentVersion) {
this.provider.triggerCompletion(documentUri.toASCIIString(),
LSPEclipseUtils.toPosition(position.getOffset(), this.document), documentVersion);
} catch (BadLocationException e) {
- CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
}
}
@@ -97,7 +98,7 @@ public void clearGhostText() {
int offset = LSPEclipseUtils.toOffset(this.completions.getTriggerPosition(), this.document);
this.triggerPosition = new Position(offset);
} catch (BadLocationException e) {
- CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
return;
}
this.completions = null;
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
index c28b40a6..b58af3f2 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/dialogs/SignInConfirmDialog.java
@@ -56,7 +56,7 @@ public void run() {
try {
this.run(true, true, task);
} catch (Exception e) {
- CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
}
}
@@ -77,7 +77,7 @@ public void run(IProgressMonitor monitor) throws InvocationTargetException, Inte
try {
return CopilotCore.getPlugin().getAuthStatusManager().signInConfirm(userCode);
} catch (Exception e) {
- CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
return null;
}
});
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
index ceaa4a5d..d0a706ad 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
@@ -185,7 +185,7 @@ public void run() {
try {
handlerService.executeCommand(commandId, null);
} catch (Exception e) {
- CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
}
}
};
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
index fdc35e98..55eea76e 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignInHandler.java
@@ -72,9 +72,9 @@ protected IStatus run(IProgressMonitor monitor) {
String msg = Messages.signInHandler_msgDialog_signInFailed;
if (StringUtils.isNotBlank(e.getMessage())) {
msg += " " + e.getMessage();
- CopilotUi.LOGGER.log(LogLevel.ERROR, msg, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, msg, e);
}
-
+
String errorMsg = "Sign in failed: " + e.getMessage();
return new Status(IStatus.ERROR, Constants.PLUGIN_ID, errorMsg);
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
index 2c446363..180d5857 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/SignOutHandler.java
@@ -49,7 +49,7 @@ private void handleSignOutException(Shell shell, Exception e) {
String msg = Messages.signOutHandler_msgDialog_signOutFailed;
if (StringUtils.isNotBlank(e.getMessage())) {
msg += " " + e.getMessage();
- CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
}
MessageDialog.openError(shell, Messages.signOutHandler_msgDialog_signOutFailedFailure, msg);
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ViewFeedbackForumHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ViewFeedbackForumHandler.java
index 9831fc82..e013f785 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ViewFeedbackForumHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ViewFeedbackForumHandler.java
@@ -4,6 +4,7 @@
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
import com.microsoft.copilot.eclipse.ui.UiConstants;
@@ -19,7 +20,7 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
try {
UiUtils.openLink(UiConstants.COPILOT_FEEDBACK_FORUM_URL);
} catch (Exception e) {
- CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
}
return null;
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
index 87448fa7..9ddc7f0c 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/UiUtils.java
@@ -20,6 +20,7 @@
import org.eclipse.ui.menus.UIElement;
import org.eclipse.ui.texteditor.ITextEditor;
+import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.logger.LogLevel;
import com.microsoft.copilot.eclipse.core.utils.PlatformUtils;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
@@ -57,7 +58,7 @@ public static boolean openLink(String link) {
IWebBrowser browser = browserSupport.createBrowser(IWorkbenchBrowserSupport.AS_EXTERNAL, null, null, null);
browser.openURL(new URI(encodedUrl).toURL());
} catch (Exception e) {
- CopilotUi.LOGGER.log(LogLevel.ERROR, e);
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
return false;
}
return true;
From 6984f8ef0984448a0a1ba8de1db7b9dcb60bacfb Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Mon, 6 Jan 2025 09:54:25 +0800
Subject: [PATCH 051/690] refactor - Separate document events handles and ghost
text rendering (#85)
---
.../ui/completion/CompletionManagerTests.java | 9 +-
.../ui/completion/EditorManagerTests.java | 18 +-
.../AcceptFullSuggestionHandlerTests.java | 16 +-
.../DiscardSuggestionHandlerTests.java | 16 +-
.../TriggerInlineSuggestionHandlerTests.java | 8 +-
com.microsoft.copilot.eclipse.ui/plugin.xml | 8 +
.../copilot/eclipse/ui/UiConstants.java | 4 +-
.../ui/completion/CompletionHandler.java | 217 -----------
.../ui/completion/CompletionManager.java | 355 ++++++++++++------
.../completion/EditorLifecycleListener.java | 6 +-
.../eclipse/ui/completion/EditorsManager.java | 36 +-
.../eclipse/ui/completion/EolGhostText.java | 32 ++
.../eclipse/ui/completion/GhostText.java | 19 +
.../eclipse/ui/completion/GhostTextType.java | 16 +
.../ui/completion/RenderingManager.java | 102 +++++
.../completion/codemining/BlockGhostText.java | 23 ++
.../codemining/GhostTextProvider.java | 52 +++
.../handlers/AcceptFullSuggestionHandler.java | 10 +-
.../eclipse/ui/handlers/CopilotHandler.java | 8 +-
.../ui/handlers/DiscardSuggestionHandler.java | 10 +-
.../TriggerInlineSuggestionHandler.java | 14 +-
21 files changed, 572 insertions(+), 407 deletions(-)
delete mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EolGhostText.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/GhostText.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/GhostTextType.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/RenderingManager.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/codemining/BlockGhostText.java
create mode 100644 com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/codemining/GhostTextProvider.java
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManagerTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManagerTests.java
index e40314ee..d0b40822 100644
--- a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManagerTests.java
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManagerTests.java
@@ -12,9 +12,8 @@
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.ITextOperationTarget;
-import org.eclipse.jface.text.ITextViewer;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.ui.IEditorPart;
@@ -75,12 +74,11 @@ public void hi() {
assertTrue(editorPart instanceof ITextEditor);
ITextEditor textEditor = (ITextEditor) editorPart;
- ITextViewer textViewer = (ITextViewer) textEditor.getAdapter(ITextOperationTarget.class);
IDocument document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
when(mockLsConnection.getDocumentVersion(any())).thenReturn(documentVersion);
- CompletionManager manager = new CompletionManager(mockLsConnection, mock(CompletionProvider.class), textViewer,
- document, file.getLocationURI());
+ CompletionManager manager = new CompletionManager(mockLsConnection, mock(CompletionProvider.class), textEditor,
+ mock(PreferenceStore.class));
List completions = List.of(new CompletionItem("uuid", " System.out.println(\"hi\");",
new Range(new Position(3, 0), new Position(3, 27)), "hi\");", new Position(3, 24), documentVersion));
@@ -103,6 +101,7 @@ protected IEditorPart getEditorPartFor(IFile file) {
ref.set(window.getActivePage().openEditor(new org.eclipse.ui.part.FileEditorInput(file),
"org.eclipse.ui.DefaultTextEditor"));
} catch (PartInitException e) {
+ // do nothing
}
}
});
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/EditorManagerTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/EditorManagerTests.java
index a4338eb8..8dc8904d 100644
--- a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/EditorManagerTests.java
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/completion/EditorManagerTests.java
@@ -27,36 +27,36 @@ class EditorManagerTests {
private IPreferenceStore mockPreferenceStore;
@Test
- void testCreateHandlerForNull() {
+ void testCreateManagerForNull() {
EditorsManager manager = new EditorsManager(mockServer, mockProvider, mockPreferenceStore);
- assertNull(manager.getOrCreateCompletionHandlerFor(null));
+ assertNull(manager.getOrCreateCompletionManagerFor(null));
}
@Test
- void testGetOrCreateCompletionHandlerForReturnsNewHandlerWhenNotPresent() {
+ void testGetOrCreateCompletionManagerForReturnsNewHandlerWhenNotPresent() {
ITextEditor mockEditor = mock(ITextEditor.class);
EditorsManager manager = new EditorsManager(mockServer, mockProvider, mockPreferenceStore);
- CompletionHandler handler = manager.getOrCreateCompletionHandlerFor(mockEditor);
+ CompletionManager completionManager = manager.getOrCreateCompletionManagerFor(mockEditor);
- assertNotNull(handler);
+ assertNotNull(completionManager);
}
@Test
- void testGetActiveHandlerWhenNoActiveEditor() {
+ void testGetActiveManagerWhenNoActiveEditor() {
EditorsManager manager = new EditorsManager(mockServer, mockProvider, mockPreferenceStore);
- assertNull(manager.getActiveCompletionHandler());
+ assertNull(manager.getActiveCompletionManager());
}
@Test
void testGetActiveHandlerWhenActiveEditor() {
ITextEditor mockEditor = mock(ITextEditor.class);
EditorsManager manager = new EditorsManager(mockServer, mockProvider, mockPreferenceStore);
- manager.getOrCreateCompletionHandlerFor(mockEditor);
+ manager.getOrCreateCompletionManagerFor(mockEditor);
manager.setActiveEditor(mockEditor);
- assertNotNull(manager.getActiveCompletionHandler());
+ assertNotNull(manager.getActiveCompletionManager());
}
}
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/AcceptFullSuggestionHandlerTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/AcceptFullSuggestionHandlerTests.java
index ebf06b18..e8126d7a 100644
--- a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/AcceptFullSuggestionHandlerTests.java
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/AcceptFullSuggestionHandlerTests.java
@@ -21,7 +21,7 @@
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
-import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
+import com.microsoft.copilot.eclipse.ui.completion.CompletionManager;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
import com.microsoft.copilot.eclipse.ui.handlers.AcceptFullSuggestionHandler;
@@ -32,10 +32,10 @@ class AcceptFullSuggestionHandlerTests {
void testIsNotEnabledWhenNoCompletionIsAvailable() {
CopilotUi mockedUi = mock(CopilotUi.class);
EditorsManager mockedManager = mock(EditorsManager.class);
- CompletionHandler mockedHandler = mock(CompletionHandler.class);
+ CompletionManager mockedCompletionManager = mock(CompletionManager.class);
when(mockedUi.getEditorsManager()).thenReturn(mockedManager);
- when(mockedManager.getActiveCompletionHandler()).thenReturn(mockedHandler);
- when(mockedHandler.hasCompletion()).thenReturn(false);
+ when(mockedManager.getActiveCompletionManager()).thenReturn(mockedCompletionManager);
+ when(mockedCompletionManager.hasCompletion()).thenReturn(false);
AcceptFullSuggestionHandler handler = new AcceptFullSuggestionHandler();
@@ -54,11 +54,11 @@ void testAcceptionNotifiedWhenCompletionIsAccepted() throws ExecutionException {
CompletionCollection completions = new CompletionCollection(
List.of(new CompletionItem("uuid", "text", null, "displayText", null, 0)), "uri");
- CompletionHandler mockedHandler = mock(CompletionHandler.class);
- doNothing().when(mockedHandler).acceptFullSuggestion();
- when(mockedHandler.getCompletions()).thenReturn(completions);
+ CompletionManager mockedCompletionManager = mock(CompletionManager.class);
+ doNothing().when(mockedCompletionManager).acceptFullSuggestion();
+ when(mockedCompletionManager.getCompletions()).thenReturn(completions);
EditorsManager mockedManager = mock(EditorsManager.class);
- when(mockedManager.getActiveCompletionHandler()).thenReturn(mockedHandler);
+ when(mockedManager.getActiveCompletionManager()).thenReturn(mockedCompletionManager);
CopilotUi mockedUi = mock(CopilotUi.class);
when(mockedUi.getEditorsManager()).thenReturn(mockedManager);
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/DiscardSuggestionHandlerTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/DiscardSuggestionHandlerTests.java
index 97061c0c..61931f91 100644
--- a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/DiscardSuggestionHandlerTests.java
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/DiscardSuggestionHandlerTests.java
@@ -20,7 +20,7 @@
import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
-import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
+import com.microsoft.copilot.eclipse.ui.completion.CompletionManager;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
import com.microsoft.copilot.eclipse.ui.handlers.DiscardSuggestionHandler;
@@ -31,10 +31,10 @@ class DiscardSuggestionHandlerTests {
void testIsNotEnabledWhenNoCompletionIsAvailable() {
CopilotUi mockedUi = mock(CopilotUi.class);
EditorsManager mockedManager = mock(EditorsManager.class);
- CompletionHandler mockedHandler = mock(CompletionHandler.class);
+ CompletionManager mockedCompletionManager = mock(CompletionManager.class);
when(mockedUi.getEditorsManager()).thenReturn(mockedManager);
- when(mockedManager.getActiveCompletionHandler()).thenReturn(mockedHandler);
- when(mockedHandler.hasCompletion()).thenReturn(false);
+ when(mockedManager.getActiveCompletionManager()).thenReturn(mockedCompletionManager);
+ when(mockedCompletionManager.hasCompletion()).thenReturn(false);
DiscardSuggestionHandler handler = new DiscardSuggestionHandler();
@@ -53,11 +53,11 @@ void testRejectionNotifiedWhenCompletionIsDiscarded() throws ExecutionException
CompletionCollection completions = mock(CompletionCollection.class);
when(completions.getUuids()).thenReturn(List.of("uuid"));
- CompletionHandler mockedHandler = mock(CompletionHandler.class);
- doNothing().when(mockedHandler).clearCompletionRendering();
- when(mockedHandler.getCompletions()).thenReturn(completions);
+ CompletionManager mockedCompletionManager = mock(CompletionManager.class);
+ doNothing().when(mockedCompletionManager).clearCompletionRendering();
+ when(mockedCompletionManager.getCompletions()).thenReturn(completions);
EditorsManager mockedManager = mock(EditorsManager.class);
- when(mockedManager.getActiveCompletionHandler()).thenReturn(mockedHandler);
+ when(mockedManager.getActiveCompletionManager()).thenReturn(mockedCompletionManager);
CopilotUi mockedUi = mock(CopilotUi.class);
when(mockedUi.getEditorsManager()).thenReturn(mockedManager);
diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/TriggerInlineSuggestionHandlerTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/TriggerInlineSuggestionHandlerTests.java
index 2aaec16e..db0e90ce 100644
--- a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/TriggerInlineSuggestionHandlerTests.java
+++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/handler/TriggerInlineSuggestionHandlerTests.java
@@ -11,7 +11,7 @@
import org.mockito.junit.jupiter.MockitoExtension;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
-import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
+import com.microsoft.copilot.eclipse.ui.completion.CompletionManager;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
import com.microsoft.copilot.eclipse.ui.handlers.TriggerInlineSuggestionHandler;
@@ -22,10 +22,10 @@ class TriggerInlineSuggestionHandlerTests {
void testIsEnabledWhenNoCompletionIsAvailable() {
CopilotUi mockedUi = mock(CopilotUi.class);
EditorsManager mockedManager = mock(EditorsManager.class);
- CompletionHandler mockedHandler = mock(CompletionHandler.class);
+ CompletionManager mockedCompletionManager = mock(CompletionManager.class);
when(mockedUi.getEditorsManager()).thenReturn(mockedManager);
- when(mockedManager.getActiveCompletionHandler()).thenReturn(mockedHandler);
- when(mockedHandler.hasCompletion()).thenReturn(false);
+ when(mockedManager.getActiveCompletionManager()).thenReturn(mockedCompletionManager);
+ when(mockedCompletionManager.hasCompletion()).thenReturn(false);
TriggerInlineSuggestionHandler handler = new TriggerInlineSuggestionHandler();
diff --git a/com.microsoft.copilot.eclipse.ui/plugin.xml b/com.microsoft.copilot.eclipse.ui/plugin.xml
index 9364b327..23eb5f58 100644
--- a/com.microsoft.copilot.eclipse.ui/plugin.xml
+++ b/com.microsoft.copilot.eclipse.ui/plugin.xml
@@ -155,4 +155,12 @@
sequence="ESC">
+
+
+
+
+
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/UiConstants.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/UiConstants.java
index da347bad..72de0ca2 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/UiConstants.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/UiConstants.java
@@ -16,8 +16,8 @@ private UiConstants() {
* Default color scale for ghost text.
*/
- public static final int DEFAULT_GHOST_TEXT_SCALE = 112;
-
+ public static final int DEFAULT_GHOST_TEXT_SCALE = 128;
+
/**
* The URL constants for the Copilot menu.
*/
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
deleted file mode 100644
index e7056b38..00000000
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionHandler.java
+++ /dev/null
@@ -1,217 +0,0 @@
-package com.microsoft.copilot.eclipse.ui.completion;
-
-import java.io.IOException;
-import java.net.URI;
-
-import org.eclipse.jface.preference.IPreferenceStore;
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.text.BadPositionCategoryException;
-import org.eclipse.jface.text.DefaultPositionUpdater;
-import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.ITextOperationTarget;
-import org.eclipse.jface.text.ITextViewer;
-import org.eclipse.jface.text.TextSelection;
-import org.eclipse.jface.util.IPropertyChangeListener;
-import org.eclipse.jface.util.PropertyChangeEvent;
-import org.eclipse.lsp4e.LSPEclipseUtils;
-import org.eclipse.swt.custom.CaretEvent;
-import org.eclipse.swt.custom.CaretListener;
-import org.eclipse.ui.texteditor.ITextEditor;
-
-import com.microsoft.copilot.eclipse.core.Constants;
-import com.microsoft.copilot.eclipse.core.CopilotCore;
-import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
-import com.microsoft.copilot.eclipse.core.completion.CompletionProvider;
-import com.microsoft.copilot.eclipse.core.logger.LogLevel;
-import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
-import com.microsoft.copilot.eclipse.ui.CopilotUi;
-import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
-import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
-
-/**
- * A class to listen events which are completion related and notify the completion manager to render the ghost text or
- * apply the suggestion to document.
- */
-public class CompletionHandler implements CaretListener, IPropertyChangeListener {
-
- private CopilotLanguageServerConnection lsConnection;
- private CompletionProvider provider;
- private ITextViewer textViewer;
- private IDocument document;
- private URI documentUri;
- private int documentVersion;
- private org.eclipse.jface.text.Position triggerPosition;
-
- private DefaultPositionUpdater positionUpdater;
- private CompletionManager completionManager;
- private boolean autoShowCompletion;
- private IPreferenceStore preferenceStore;
-
- /**
- * Creates a new completion handler.
- */
- public CompletionHandler(CopilotLanguageServerConnection lsConnection, CompletionProvider provider,
- ITextEditor editor, IPreferenceStore preferenceStore) {
- this.lsConnection = lsConnection;
- this.textViewer = (ITextViewer) editor.getAdapter(ITextOperationTarget.class);
- // if the text viewer is null, we will not register listeners.
- // the side effect is that the completion will not be triggered for this editor.
- if (textViewer == null) {
- CopilotCore.LOGGER.log(LogLevel.INFO, "Text viewer is null for editor: " + editor.getTitle());
- return;
- }
- this.document = LSPEclipseUtils.getDocument(editor);
- if (this.document == null) {
- CopilotCore.LOGGER.log(LogLevel.INFO, "Document is null for editor: " + editor.getTitle());
- return;
- }
- this.documentUri = LSPEclipseUtils.toUri(document);
- if (this.documentUri == null) {
- CopilotCore.LOGGER.log(LogLevel.INFO, "Document URI is null for editor: " + editor.getTitle());
- return;
- }
- try {
- lsConnection.connectDocument(this.document);
- } catch (IOException e) {
- CopilotCore.LOGGER.log(LogLevel.ERROR, e);
- return;
- }
- this.documentVersion = -1;
- this.triggerPosition = new org.eclipse.jface.text.Position(0);
- this.completionManager = new CompletionManager(lsConnection, provider, this.textViewer, this.document,
- this.documentUri);
- registerListeners();
-
- // position updater is used to update the position when the document is changed.
- // this is needed because the completion ghost text is rendered based on the
- // position in the document. If the document is changed, the position will be
- // invalidated.
- this.positionUpdater = new DefaultPositionUpdater(this.getCategory());
- this.document.addPositionCategory(this.getCategory());
- this.document.addPositionUpdater(this.positionUpdater);
-
- // initialize the auto show completion preference and add listener to update it.
- this.preferenceStore = preferenceStore;
- this.autoShowCompletion = preferenceStore.getBoolean(Constants.AUTO_SHOW_COMPLETION);
- preferenceStore.addPropertyChangeListener(this);
- }
-
- /**
- * Check if the completion handler has any completion suggestions.
- */
- public boolean hasCompletion() {
- return this.completionManager.hasCompletion();
- }
-
- /**
- * Accept the full completion suggestion.
- */
- public void acceptFullSuggestion() {
- try {
- this.document.addPosition(this.triggerPosition);
- this.completionManager.acceptSuggestion();
- this.document.removePosition(this.triggerPosition);
- } catch (BadLocationException e) {
- CopilotCore.LOGGER.log(LogLevel.ERROR, e);
- return;
- }
- this.clearCompletionRendering();
- SwtUtils.invokeOnDisplayThread(() -> {
- this.textViewer.getSelectionProvider().setSelection(new TextSelection(this.triggerPosition.offset, 0));
- }, this.textViewer.getTextWidget());
- }
-
- void registerListeners() {
- SwtUtils.invokeOnDisplayThread(() -> {
- this.textViewer.getTextWidget().addCaretListener(this);
- });
- }
-
- /**
- * Trigger the inline completion.
- */
- public void triggerCompletion() {
- clearCompletionRendering();
- this.completionManager.triggerCompletion(this.triggerPosition, this.documentVersion);
- }
-
- /**
- * Clear the completion ghost text.
- */
- public void clearCompletionRendering() {
- this.completionManager.clearGhostText();
- }
-
- public CompletionCollection getCompletions() {
- return this.completionManager.getCompletions();
- }
-
- @Override
- public void caretMoved(CaretEvent event) {
- int modelOffset = UiUtils.widgetOffset2ModelOffset(textViewer, event.caretOffset);
- this.triggerPosition = new org.eclipse.jface.text.Position(modelOffset);
-
- // it's guaranteed that the document change event comes earlier than caret
- // change event. See org.eclipse.swt.custom.StyledText#modifyContent()
- int currentVersion = this.lsConnection.getDocumentVersion(this.documentUri);
-
- // initialize the document version and return. This avoids the ghost text
- // being rendered when user opens the editor and just clicks in it.
- if (this.documentVersion < 0) {
- this.documentVersion = currentVersion;
- return;
- }
- if (currentVersion == this.documentVersion) {
- // if the caret position is changed without document version change, we should remove the ghost text.
- clearCompletionRendering();
- } else {
- this.documentVersion = currentVersion;
- if (this.autoShowCompletion) {
- triggerCompletion();
- }
- }
-
- }
-
- @Override
- public void propertyChange(PropertyChangeEvent event) {
- if (event.getProperty().equals(Constants.AUTO_SHOW_COMPLETION)) {
- this.autoShowCompletion = Boolean.parseBoolean(event.getNewValue().toString());
- }
- }
-
- /**
- * Get category for the position updater of this document.
- */
- private String getCategory() {
- return "GCE-" + this.documentUri.toASCIIString();
- }
-
- /**
- * Disposes the resources of this completion handler.
- */
- public void dispose() {
- if (this.completionManager == null) {
- // null manager means the handler is not initialized.
- return;
- }
-
- this.completionManager.dispose();
- this.completionManager = null;
- preferenceStore.removePropertyChangeListener(this);
- lsConnection.disconnectDocument(this.documentUri);
- try {
- this.document.removePositionCategory(this.getCategory());
- } catch (BadPositionCategoryException e) {
- CopilotCore.LOGGER.log(LogLevel.ERROR, e);
- }
- this.document.removePositionUpdater(this.positionUpdater);
- SwtUtils.invokeOnDisplayThread(() -> {
- if (this.textViewer.getTextWidget() != null) {
- this.textViewer.getTextWidget().removeCaretListener(this);
- }
- });
-
- }
-
-}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
index b38235c1..d2756521 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/CompletionManager.java
@@ -1,23 +1,32 @@
package com.microsoft.copilot.eclipse.ui.completion;
+import java.io.IOException;
import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextViewer;
-import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.TextSelection;
+import org.eclipse.jface.text.codemining.ICodeMining;
+import org.eclipse.jface.text.source.ISourceViewerExtension5;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.lsp4e.LSPEclipseUtils;
+import org.eclipse.swt.custom.CaretEvent;
+import org.eclipse.swt.custom.CaretListener;
import org.eclipse.swt.custom.StyledText;
-import org.eclipse.swt.events.PaintEvent;
-import org.eclipse.swt.events.PaintListener;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.RGB;
-import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.ui.texteditor.ITextEditor;
+import com.microsoft.copilot.eclipse.core.Constants;
import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
import com.microsoft.copilot.eclipse.core.completion.CompletionListener;
@@ -26,87 +35,123 @@
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyShownParams;
-import com.microsoft.copilot.eclipse.ui.CopilotUi;
-import com.microsoft.copilot.eclipse.ui.UiConstants;
+import com.microsoft.copilot.eclipse.ui.completion.codemining.BlockGhostText;
import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
/**
- * A class to control completion rendering.
+ * A class to listen events which are completion related and notify the completion manager to render the ghost text or
+ * apply the suggestion to document.
*/
-public class CompletionManager implements CompletionListener, PaintListener {
+public class CompletionManager implements CaretListener, CompletionListener, IPropertyChangeListener {
private CopilotLanguageServerConnection lsConnection;
private CompletionProvider provider;
+ private CompletionCollection completions;
+ private ITextViewer textViewer;
+ private StyledText styledText;
private IDocument document;
private URI documentUri;
- private CompletionCollection completions;
+ private int documentVersion;
+ private org.eclipse.jface.text.Position triggerPosition;
+ private List codeMinings;
- private ITextViewer textViewer;
- private Color ghostTextColor;
- private Position triggerPosition;
+ private DefaultPositionUpdater positionUpdater;
+ private RenderingManager renderingManager;
+ private boolean autoShowCompletion;
+ private IPreferenceStore preferenceStore;
/**
- * Creates a new CompletionManager.
+ * Creates a new completion manager. The manager is responsible for trigger the completion, apply suggestions to the
+ * document. And schedule the rendering of ghost text.
*/
public CompletionManager(CopilotLanguageServerConnection lsConnection, CompletionProvider provider,
- ITextViewer textViewer, IDocument document, URI documentUri) {
+ ITextEditor editor, IPreferenceStore preferenceStore) {
+ this.codeMinings = new ArrayList<>();
+ this.textViewer = (ITextViewer) editor.getAdapter(ITextOperationTarget.class);
+ // if the text viewer is null, we will not register listeners.
+ // the side effect is that the completion will not be triggered for this editor.
+ if (textViewer == null) {
+ CopilotCore.LOGGER.log(LogLevel.INFO, "Text viewer is null for editor: " + editor.getTitle());
+ return;
+ }
+ this.styledText = this.textViewer.getTextWidget();
+ if (this.styledText == null) {
+ CopilotCore.LOGGER.log(LogLevel.INFO, "Styled text is null for editor: " + editor.getTitle());
+ return;
+ }
+ this.document = LSPEclipseUtils.getDocument(editor);
+ if (this.document == null) {
+ CopilotCore.LOGGER.log(LogLevel.INFO, "Document is null for editor: " + editor.getTitle());
+ return;
+ }
+ this.documentUri = LSPEclipseUtils.toUri(document);
+ if (this.documentUri == null) {
+ CopilotCore.LOGGER.log(LogLevel.INFO, "Document URI is null for editor: " + editor.getTitle());
+ return;
+ }
+ try {
+ lsConnection.connectDocument(this.document);
+ } catch (IOException e) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
+ return;
+ }
+
+ this.renderingManager = new RenderingManager(this.textViewer);
+
this.lsConnection = lsConnection;
this.provider = provider;
this.provider.addCompletionListener(this);
- this.document = document;
- this.documentUri = documentUri;
this.completions = null;
+ this.documentVersion = -1;
+ this.triggerPosition = new org.eclipse.jface.text.Position(0);
- this.triggerPosition = new Position(0);
- this.textViewer = textViewer;
- StyledText styledText = textViewer.getTextWidget();
- if (styledText != null) {
- SwtUtils.invokeOnDisplayThread(() -> {
- styledText.addPaintListener(this);
- this.ghostTextColor = new Color(styledText.getDisplay(), new RGB(UiConstants.DEFAULT_GHOST_TEXT_SCALE,
- UiConstants.DEFAULT_GHOST_TEXT_SCALE, UiConstants.DEFAULT_GHOST_TEXT_SCALE));
- }, styledText);
- }
+ // initialize the auto show completion preference and add listener to update it.
+ this.preferenceStore = preferenceStore;
+ this.autoShowCompletion = preferenceStore.getBoolean(Constants.AUTO_SHOW_COMPLETION);
+
+ // position updater is used to update the position when the document is changed.
+ // this is needed because the completion ghost text is rendered based on the
+ // position in the document. If the document is changed, the position will be
+ // invalidated.
+ this.positionUpdater = new DefaultPositionUpdater(this.getCategory());
+ this.document.addPositionCategory(this.getCategory());
+ this.document.addPositionUpdater(this.positionUpdater);
+
+ registerListeners();
}
- /**
- * Triggers the completion.
- */
- public void triggerCompletion(Position position, int documentVersion) {
- this.triggerPosition = position;
- try {
- this.provider.triggerCompletion(documentUri.toASCIIString(),
- LSPEclipseUtils.toPosition(position.getOffset(), this.document), documentVersion);
- } catch (BadLocationException e) {
- CopilotCore.LOGGER.log(LogLevel.ERROR, e);
- }
+ void registerListeners() {
+ SwtUtils.invokeOnDisplayThread(() -> {
+ this.styledText.addCaretListener(this);
+ }, this.styledText);
+
+ this.preferenceStore.addPropertyChangeListener(this);
}
- /**
- * Clear the completion.
- */
- public void clearGhostText() {
- if (this.completions == null || this.completions.getSize() == 0) {
- return;
- }
- try {
- // use completion trigger position if available. this.triggerPosition is the current
- // cursor position, which may not be the same as the completion trigger position when user
- // use mouse to move the cursor. In that case, the line vertical indentation might not be
- // reset correctly.
- int offset = LSPEclipseUtils.toOffset(this.completions.getTriggerPosition(), this.document);
- this.triggerPosition = new Position(offset);
- } catch (BadLocationException e) {
- CopilotCore.LOGGER.log(LogLevel.ERROR, e);
+ @Override
+ public void caretMoved(CaretEvent event) {
+ int modelOffset = UiUtils.widgetOffset2ModelOffset(textViewer, event.caretOffset);
+ this.triggerPosition = new org.eclipse.jface.text.Position(modelOffset);
+
+ // it's guaranteed that the document change event comes earlier than caret
+ // change event. See org.eclipse.swt.custom.StyledText#modifyContent()
+ int currentVersion = this.lsConnection.getDocumentVersion(this.documentUri);
+
+ // initialize the document version and return. This avoids the ghost text
+ // being rendered when user opens the editor and just clicks in it.
+ if (this.documentVersion < 0) {
+ this.documentVersion = currentVersion;
return;
}
- this.completions = null;
- StyledText styledText = textViewer.getTextWidget();
- if (styledText != null) {
- this.setLineVerticalIndentation(styledText, null,
- UiUtils.modelOffset2WidgetOffset(textViewer, this.triggerPosition.getOffset()));
- SwtUtils.invokeOnDisplayThread(styledText::redraw, styledText);
+ if (currentVersion == this.documentVersion) {
+ // if the caret position is changed without document version change, we should remove the ghost text.
+ clearCompletionRendering();
+ } else {
+ this.documentVersion = currentVersion;
+ if (this.autoShowCompletion) {
+ triggerCompletion();
+ }
}
}
@@ -122,71 +167,103 @@ public void onCompletionResolved(CompletionCollection completions) {
}
this.completions = completions;
- StyledText styledText = textViewer.getTextWidget();
- if (styledText != null) {
- SwtUtils.invokeOnDisplayThread(styledText::redraw, styledText);
- this.notifyShown();
- }
- }
- @Override
- public void paintControl(PaintEvent e) {
- StyledText styledText = textViewer.getTextWidget();
- if (styledText == null) {
- return;
+ // render the first line by ourself to make sure cursor position not change.
+ List ghostTexts = resolveGhostTexts();
+ if (!ghostTexts.isEmpty()) {
+ this.renderingManager.setGhostTexts(ghostTexts);
+ this.renderingManager.redraw();
+ this.notifyShown();
}
- GC gc = e.gc;
- int widgetOffset = UiUtils.modelOffset2WidgetOffset(textViewer, this.triggerPosition.getOffset());
- // will get index out of bounds if the cursor is at the end.
- // Because there is no more text to get bounds at EOF.
- widgetOffset = Math.max(Math.min(widgetOffset, styledText.getCharCount() - 1), 0);
- setLineVerticalIndentation(styledText, gc, widgetOffset);
+ // render the remaining lines by code mining api.
+ resolveCodeMiningGhostTexts();
+ this.updateCodeMinings();
+ }
- if (this.completions == null) {
- return;
+ private List resolveGhostTexts() {
+ if (this.completions == null || this.completions.getSize() == 0) {
+ return Collections.emptyList();
}
-
- gc.setForeground(this.ghostTextColor);
+ List ghostTexts = new ArrayList<>();
String firstLine = this.completions.getFirstLine();
if (StringUtils.isNotBlank(firstLine)) {
- Rectangle bounds = styledText.getTextBounds(widgetOffset, widgetOffset);
- int y = bounds.y;
- y += bounds.height - styledText.getLineHeight();
- gc.drawString(firstLine, bounds.x + bounds.width, y, true);
+ ghostTexts.add(new EolGhostText(firstLine, triggerPosition.getOffset()));
+ }
+ return ghostTexts;
+ }
+
+ private void resolveCodeMiningGhostTexts() {
+ if (this.completions == null || this.completions.getSize() == 0) {
+ this.codeMinings.clear();
+ return;
}
+ List cm = new ArrayList<>();
String remainingLines = this.completions.getRemainingLines();
- if (StringUtils.isNotBlank(remainingLines)) {
- int lineHeight = styledText.getLineHeight();
- int fontHeight = gc.getFontMetrics().getHeight();
- int x = styledText.getLeftMargin();
- Point offsetLocation = styledText.getLocationAtOffset(widgetOffset);
- int y = offsetLocation.y + lineHeight * 2 - fontHeight;
- gc.drawText(remainingLines, x, y, true);
+ if (StringUtils.isNotEmpty(remainingLines)) {
+ try {
+ cm.add(
+ new BlockGhostText(document.getLineOfOffset(triggerPosition.offset) + 1, document, null, remainingLines));
+ } catch (BadLocationException e) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
+ }
}
+ this.codeMinings = cm;
+ }
+ private void updateCodeMinings() {
+ if (textViewer instanceof ISourceViewerExtension5 sve) {
+ sve.updateCodeMinings();
+ }
}
- private void setLineVerticalIndentation(StyledText styledText, GC gc, int widgetOffset) {
- int height = 0;
- if (this.completions != null && gc != null) {
- // Change the height (line vertical indentation) to fit the line of
- // ghost text.
- Point ghostTextExtent = gc.textExtent(this.completions.getText());
- int numberOfLines = this.completions.getNumberOfLines();
- height = ghostTextExtent.y - ghostTextExtent.y / numberOfLines;
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(Constants.AUTO_SHOW_COMPLETION)) {
+ this.autoShowCompletion = Boolean.parseBoolean(event.getNewValue().toString());
}
+ }
- int lineIndex = styledText.getLineAtOffset(widgetOffset) + 1;
- lineIndex = Math.min(lineIndex, styledText.getLineCount() - 1);
- styledText.setLineVerticalIndent(lineIndex, height);
+ /**
+ * Trigger the inline completion.
+ */
+ public void triggerCompletion() {
+ clearCompletionRendering();
+ try {
+ this.provider.triggerCompletion(documentUri.toASCIIString(),
+ LSPEclipseUtils.toPosition(this.triggerPosition.getOffset(), this.document), documentVersion);
+ } catch (BadLocationException e) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
+ }
}
/**
- * Return if the completion manager has completion rendering.
+ * Clear the completion ghost text.
*/
- public boolean hasCompletion() {
- return this.completions != null;
+ public void clearCompletionRendering() {
+ this.codeMinings.clear();
+ this.updateCodeMinings();
+
+ this.renderingManager.clearGhostText();
+ this.completions = null;
+ }
+
+ /**
+ * Accept the full completion suggestion.
+ */
+ public void acceptFullSuggestion() {
+ try {
+ this.document.addPosition(this.triggerPosition);
+ this.acceptSuggestion();
+ this.document.removePosition(this.triggerPosition);
+ } catch (BadLocationException e) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
+ return;
+ }
+ this.clearCompletionRendering();
+ SwtUtils.invokeOnDisplayThread(() -> {
+ this.textViewer.getSelectionProvider().setSelection(new TextSelection(this.triggerPosition.offset, 0));
+ }, this.textViewer.getTextWidget());
}
/**
@@ -194,7 +271,7 @@ public boolean hasCompletion() {
*
* @throws BadLocationException if the offset is invalid.
*/
- public void acceptSuggestion() throws BadLocationException {
+ void acceptSuggestion() throws BadLocationException {
if (this.completions == null || this.completions.getSize() == 0) {
return;
}
@@ -207,22 +284,60 @@ public void acceptSuggestion() throws BadLocationException {
this.document.replace(startOffset, endOffset - startOffset, text);
}
- public CompletionCollection getCompletions() {
- return completions;
+ /**
+ * Get category for the position updater of this document.
+ */
+ private String getCategory() {
+ return "GCE-" + this.documentUri.toASCIIString();
}
/**
- * Dispose the resources used by the completion manager.
+ * Disposes the resources of this completion handler.
*/
public void dispose() {
- this.provider.removeCompletionListener(this);
- this.completions = null;
- if (this.ghostTextColor != null) {
- this.ghostTextColor.dispose();
- this.ghostTextColor = null;
+ if (this.provider != null) {
+ this.provider.removeCompletionListener(this);
+ }
+ if (this.renderingManager != null) {
+ this.renderingManager.dispose();
+ this.renderingManager = null;
+ }
+
+ if (this.preferenceStore != null) {
+ preferenceStore.removePropertyChangeListener(this);
+ }
+ lsConnection.disconnectDocument(this.documentUri);
+
+ if (this.document != null) {
+ try {
+ this.document.removePositionCategory(this.getCategory());
+ } catch (BadPositionCategoryException e) {
+ CopilotCore.LOGGER.log(LogLevel.ERROR, e);
+ }
+ this.document.removePositionUpdater(this.positionUpdater);
+ }
+
+ if (this.styledText != null) {
+ SwtUtils.invokeOnDisplayThread(() -> {
+ this.styledText.removeCaretListener(this);
+ });
}
}
+ /**
+ * Will be used when notifying the completion rejection/acceptance.
+ */
+ public CompletionCollection getCompletions() {
+ return this.completions;
+ }
+
+ /**
+ * Check if the completion handler has any completion suggestions.
+ */
+ public boolean hasCompletion() {
+ return this.completions != null;
+ }
+
private void notifyShown() {
if (this.completions == null || this.completions.getSize() == 0) {
return;
@@ -237,4 +352,8 @@ private void notifyShown() {
this.lsConnection.notifyShown(params);
}
+ public List getCodeMinings() {
+ return codeMinings;
+ }
+
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorLifecycleListener.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorLifecycleListener.java
index 17f477e7..2557e488 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorLifecycleListener.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorLifecycleListener.java
@@ -46,14 +46,14 @@ public void partOpened(IWorkbenchPart part) {
}
/**
- * Creates the {@link CompletionHandler} for the ITextEditor of the IWorkbenchPart.
+ * Creates the {@link CompletionManager} for the ITextEditor of the IWorkbenchPart.
*/
public void createCompletionHandlerFor(IWorkbenchPart part) {
IEditorPart editorPart = part.getAdapter(IEditorPart.class);
if (editorPart != null) {
ITextEditor editor = editorPart.getAdapter(ITextEditor.class);
if (editor != null) {
- manager.getOrCreateCompletionHandlerFor(editor);
+ manager.getOrCreateCompletionManagerFor(editor);
manager.setActiveEditor(editor);
}
}
@@ -64,7 +64,7 @@ void disposeCompletionHandlerFor(IWorkbenchPart part) {
if (editorPart != null) {
ITextEditor editor = editorPart.getAdapter(ITextEditor.class);
if (editor != null) {
- manager.disposeCompletionHandlerFor(editor);
+ manager.disposeCompletionManagerFor(editor);
if (editor.equals(manager.getActiveEditor())) {
manager.setActiveEditor(null);
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorsManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorsManager.java
index 6a042971..235e605e 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorsManager.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EditorsManager.java
@@ -12,13 +12,13 @@
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
/**
- * Manages the completion handlers for all available ITextEditors.
+ * Manages the completion managers for all available ITextEditors.
*/
public class EditorsManager {
private CopilotLanguageServerConnection languageServer;
private CompletionProvider completionProvider;
- private Map editorMap;
+ private Map editorMap;
private AtomicReference activeEditor;
private IPreferenceStore preferenceStore;
@@ -35,25 +35,37 @@ public EditorsManager(CopilotLanguageServerConnection languageServer, Completion
}
/**
- * Gets the {@link com.microsoft.copilot.eclipse.ui.completion.CompletionHandler CompletionHandler} for the given
+ * Gets the {@link com.microsoft.copilot.eclipse.ui.completion.CompletionManager CompletionManager} for the given
* ITextEditor. If it does not exist, a new one will be created. Returns null if the editor is
* null.
*/
- public CompletionHandler getOrCreateCompletionHandlerFor(ITextEditor editor) {
+ public CompletionManager getOrCreateCompletionManagerFor(ITextEditor editor) {
if (editor == null) {
return null;
}
return editorMap.computeIfAbsent(editor,
- edt -> new CompletionHandler(this.languageServer, this.completionProvider, edt, this.preferenceStore));
+ edt -> new CompletionManager(this.languageServer, this.completionProvider, edt, this.preferenceStore));
}
/**
- * Gets the {@link com.microsoft.copilot.eclipse.ui.completion.CompletionHandler CompletionHandler} for the active
+ * Gets the {@link com.microsoft.copilot.eclipse.ui.completion.CompletionManager CompletionManager} for the given
+ * ITextEditor. Returns null if there is no manager for the editor.
+ */
+ public CompletionManager getCompletionManagerFor(ITextEditor editor) {
+ if (editor == null) {
+ return null;
+ }
+
+ return editorMap.get(editor);
+ }
+
+ /**
+ * Gets the {@link com.microsoft.copilot.eclipse.ui.completion.CompletionManager CompletionManager} for the active
* ITextEditor.
*/
@Nullable
- public CompletionHandler getActiveCompletionHandler() {
+ public CompletionManager getActiveCompletionManager() {
if (this.activeEditor.get() == null) {
return null;
}
@@ -61,11 +73,11 @@ public CompletionHandler getActiveCompletionHandler() {
}
/**
- * Disposes the {@link com.microsoft.copilot.eclipse.ui.completion.CompletionHandler CompletionHandler} for the given
+ * Disposes the {@link com.microsoft.copilot.eclipse.ui.completion.CompletionManager CompletionHandler} for the given
* ITextEditor.
*/
- public void disposeCompletionHandlerFor(ITextEditor editor) {
- CompletionHandler handler = editorMap.remove(editor);
+ public void disposeCompletionManagerFor(ITextEditor editor) {
+ CompletionManager handler = editorMap.remove(editor);
if (handler != null) {
handler.dispose();
}
@@ -84,10 +96,10 @@ public ITextEditor getActiveEditor() {
}
/**
- * Dispose all the handlers.
+ * Dispose all the managers.
*/
public void dispose() {
- for (CompletionHandler handler : this.editorMap.values()) {
+ for (CompletionManager handler : this.editorMap.values()) {
handler.dispose();
}
this.editorMap.clear();
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EolGhostText.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EolGhostText.java
new file mode 100644
index 00000000..4bc2a905
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/EolGhostText.java
@@ -0,0 +1,32 @@
+package com.microsoft.copilot.eclipse.ui.completion;
+
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+
+/**
+ * A ghost text placed at the end of the line. For single line ghost text, we draw it by ourselves. Because the code
+ * mining API will put the cursor at the end of the line, which is not what we want.
+ */
+public class EolGhostText extends GhostText {
+
+ /**
+ * Creates a new EolGhostText.
+ */
+ protected EolGhostText(String text, int modelOffset) {
+ super(text, modelOffset, GhostTextType.END_OF_LINE);
+ }
+
+ @Override
+ public void draw(StyledText styledText, int widgetOffset, GC gc) {
+ if (StringUtils.isNotBlank(this.text)) {
+ Rectangle bounds = styledText.getTextBounds(widgetOffset, widgetOffset);
+ int y = bounds.y;
+ y += bounds.height - styledText.getLineHeight();
+ gc.drawString(this.text, bounds.x + bounds.width, y, true);
+ }
+
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/GhostText.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/GhostText.java
new file mode 100644
index 00000000..a690b93a
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/GhostText.java
@@ -0,0 +1,19 @@
+package com.microsoft.copilot.eclipse.ui.completion;
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.GC;
+
+abstract class GhostText {
+ protected String text;
+ protected int modelOffset;
+ protected GhostTextType type;
+
+ protected GhostText(String text, int modelOffset, GhostTextType type) {
+ super();
+ this.text = text;
+ this.modelOffset = modelOffset;
+ this.type = type;
+ }
+
+ public abstract void draw(StyledText styledText, int widgetOffset, GC gc);
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/GhostTextType.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/GhostTextType.java
new file mode 100644
index 00000000..cca031d8
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/GhostTextType.java
@@ -0,0 +1,16 @@
+package com.microsoft.copilot.eclipse.ui.completion;
+
+/**
+ * Type of ghost text.
+ */
+public enum GhostTextType {
+ /**
+ * Single line of ghost text placed in the line (not at the end).
+ */
+ IN_LINE,
+
+ /**
+ * Single line of ghost text placed at the end of the line.
+ */
+ END_OF_LINE
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/RenderingManager.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/RenderingManager.java
new file mode 100644
index 00000000..df1a5862
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/RenderingManager.java
@@ -0,0 +1,102 @@
+package com.microsoft.copilot.eclipse.ui.completion;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.RGB;
+
+import com.microsoft.copilot.eclipse.ui.UiConstants;
+import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
+import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
+
+/**
+ * A class to control completion rendering.
+ */
+public class RenderingManager implements PaintListener {
+
+ private List ghostTexts;
+
+ private ITextViewer textViewer;
+ private Color ghostTextColor;
+
+ /**
+ * Creates a new CompletionManager.
+ */
+ public RenderingManager(ITextViewer textViewer) {
+ this.ghostTexts = new ArrayList<>();
+ this.textViewer = textViewer;
+ StyledText styledText = textViewer.getTextWidget();
+ if (styledText != null) {
+ SwtUtils.invokeOnDisplayThread(() -> {
+ styledText.addPaintListener(this);
+ this.ghostTextColor = new Color(styledText.getDisplay(), new RGB(UiConstants.DEFAULT_GHOST_TEXT_SCALE,
+ UiConstants.DEFAULT_GHOST_TEXT_SCALE, UiConstants.DEFAULT_GHOST_TEXT_SCALE));
+ }, styledText);
+ }
+ }
+
+ /**
+ * Redraw the canvas(editor).
+ */
+ public void redraw() {
+ StyledText styledText = textViewer.getTextWidget();
+ if (styledText != null) {
+ SwtUtils.invokeOnDisplayThread(styledText::redraw, styledText);
+ }
+ }
+
+ @Override
+ public void paintControl(PaintEvent e) {
+ StyledText styledText = textViewer.getTextWidget();
+ if (styledText == null) {
+ return;
+ }
+
+ if (this.ghostTexts == null || this.ghostTexts.isEmpty()) {
+ return;
+ }
+
+ GC gc = e.gc;
+ gc.setForeground(this.ghostTextColor);
+
+ int widgetOffset = UiUtils.modelOffset2WidgetOffset(textViewer, this.ghostTexts.get(0).modelOffset);
+ // will get index out of bounds if the cursor is at the end.
+ // Because there is no more text to get bounds at EOF.
+ widgetOffset = Math.max(Math.min(widgetOffset, styledText.getCharCount() - 1), 0);
+ for (GhostText ghostText : this.ghostTexts) {
+ ghostText.draw(styledText, widgetOffset, gc);
+ }
+ }
+
+ /**
+ * Clear the ghost texts.
+ */
+ public void clearGhostText() {
+ this.ghostTexts.clear();
+ StyledText styledText = textViewer.getTextWidget();
+ if (styledText != null) {
+ SwtUtils.invokeOnDisplayThread(styledText::redraw, styledText);
+ }
+ }
+
+ /**
+ * Dispose the resources used by the completion manager.
+ */
+ public void dispose() {
+ if (this.ghostTextColor != null) {
+ this.ghostTextColor.dispose();
+ this.ghostTextColor = null;
+ }
+ }
+
+ public void setGhostTexts(List ghostTexts) {
+ this.ghostTexts = ghostTexts;
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/codemining/BlockGhostText.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/codemining/BlockGhostText.java
new file mode 100644
index 00000000..9e1271bc
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/codemining/BlockGhostText.java
@@ -0,0 +1,23 @@
+package com.microsoft.copilot.eclipse.ui.completion.codemining;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.codemining.ICodeMiningProvider;
+import org.eclipse.jface.text.codemining.LineHeaderCodeMining;
+import org.eclipse.jface.text.source.inlined.Positions;
+
+/**
+ * A block of ghost text with multiple lines placed in new lines. We use code mining API to display the ghost text.
+ */
+public class BlockGhostText extends LineHeaderCodeMining {
+
+ /**
+ * Creates a new BlockGhostText.
+ */
+ public BlockGhostText(int beforeLineNumber, IDocument document, ICodeMiningProvider provider, String text)
+ throws BadLocationException {
+ super(Positions.of(beforeLineNumber, document, false), provider, null);
+ this.setLabel(text);
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/codemining/GhostTextProvider.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/codemining/GhostTextProvider.java
new file mode 100644
index 00000000..14885e3d
--- /dev/null
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/completion/codemining/GhostTextProvider.java
@@ -0,0 +1,52 @@
+package com.microsoft.copilot.eclipse.ui.completion.codemining;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider;
+import org.eclipse.jface.text.codemining.ICodeMining;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import com.microsoft.copilot.eclipse.ui.CopilotUi;
+import com.microsoft.copilot.eclipse.ui.completion.CompletionManager;
+import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
+
+/**
+ * A provider for ghost text.
+ */
+public class GhostTextProvider extends AbstractCodeMiningProvider {
+
+ @Override
+ public CompletableFuture> provideCodeMinings(ITextViewer viewer,
+ IProgressMonitor monitor) {
+ return CompletableFuture.completedFuture(getCodeMinings());
+ }
+
+ @Nullable
+ private List getCodeMinings() {
+ ITextEditor belongingEditor = this.getAdapter(ITextEditor.class);
+ if (belongingEditor == null) {
+ return Collections.emptyList();
+ }
+ CopilotUi copilotUi = CopilotUi.getPlugin();
+ if (copilotUi == null) {
+ return Collections.emptyList();
+ }
+ EditorsManager editorsManager = copilotUi.getEditorsManager();
+ if (editorsManager == null) {
+ return Collections.emptyList();
+ }
+ CompletionManager manager = editorsManager.getCompletionManagerFor(belongingEditor);
+
+ if (manager == null) {
+ return Collections.emptyList();
+ }
+
+ return manager.getCodeMinings();
+ }
+
+}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/AcceptFullSuggestionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/AcceptFullSuggestionHandler.java
index ed356dd9..5b9c118f 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/AcceptFullSuggestionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/AcceptFullSuggestionHandler.java
@@ -6,7 +6,7 @@
import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.CompletionItem;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyAcceptedParams;
-import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
+import com.microsoft.copilot.eclipse.ui.completion.CompletionManager;
/**
* Handler for accepting the full suggestion.
@@ -15,7 +15,7 @@ public class AcceptFullSuggestionHandler extends CopilotHandler {
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
- CompletionHandler handler = getActiveCompletionHandler();
+ CompletionManager handler = getActiveCompletionManager();
if (handler != null) {
notifyAccepted(handler.getCompletions());
handler.acceptFullSuggestion();
@@ -25,9 +25,9 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
@Override
public boolean isEnabled() {
- CompletionHandler handler = getActiveCompletionHandler();
- if (handler != null) {
- return handler.hasCompletion();
+ CompletionManager manager = getActiveCompletionManager();
+ if (manager != null) {
+ return manager.hasCompletion();
}
return false;
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java
index be347e3b..5c057566 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/CopilotHandler.java
@@ -7,7 +7,7 @@
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.ui.CopilotStatusManager;
import com.microsoft.copilot.eclipse.ui.CopilotUi;
-import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
+import com.microsoft.copilot.eclipse.ui.completion.CompletionManager;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
/**
@@ -15,10 +15,10 @@
*/
public abstract class CopilotHandler extends AbstractHandler {
/**
- * Gets the active {@link CompletionHandler} for the current editor.
+ * Gets the active {@link CompletionManager} for the current editor.
*/
@Nullable
- public CompletionHandler getActiveCompletionHandler() {
+ public CompletionManager getActiveCompletionManager() {
CopilotUi copilotUi = CopilotUi.getPlugin();
if (copilotUi == null) {
return null;
@@ -27,7 +27,7 @@ public CompletionHandler getActiveCompletionHandler() {
if (manager == null) {
return null;
}
- return manager.getActiveCompletionHandler();
+ return manager.getActiveCompletionManager();
}
public CopilotLanguageServerConnection getLanguageServerConnection() {
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/DiscardSuggestionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/DiscardSuggestionHandler.java
index 2eba32cf..9c8a650f 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/DiscardSuggestionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/DiscardSuggestionHandler.java
@@ -7,7 +7,7 @@
import com.microsoft.copilot.eclipse.core.completion.CompletionCollection;
import com.microsoft.copilot.eclipse.core.lsp.protocol.NotifyRejectedParams;
-import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
+import com.microsoft.copilot.eclipse.ui.completion.CompletionManager;
/**
* Handler for clearing the completion ghost text.
@@ -15,7 +15,7 @@
public class DiscardSuggestionHandler extends CopilotHandler {
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
- CompletionHandler handler = getActiveCompletionHandler();
+ CompletionManager handler = getActiveCompletionManager();
if (handler != null) {
notifyRejected(handler.getCompletions());
handler.clearCompletionRendering();
@@ -25,9 +25,9 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
@Override
public boolean isEnabled() {
- CompletionHandler handler = getActiveCompletionHandler();
- if (handler != null) {
- return handler.hasCompletion();
+ CompletionManager manager = getActiveCompletionManager();
+ if (manager != null) {
+ return manager.hasCompletion();
}
return false;
}
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/TriggerInlineSuggestionHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/TriggerInlineSuggestionHandler.java
index 1315f87a..ae2cd192 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/TriggerInlineSuggestionHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/TriggerInlineSuggestionHandler.java
@@ -3,7 +3,7 @@
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
-import com.microsoft.copilot.eclipse.ui.completion.CompletionHandler;
+import com.microsoft.copilot.eclipse.ui.completion.CompletionManager;
/**
* Handler for triggering the inline suggestion.
@@ -12,18 +12,18 @@ public class TriggerInlineSuggestionHandler extends CopilotHandler {
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
- CompletionHandler handler = getActiveCompletionHandler();
- if (handler != null) {
- handler.triggerCompletion();
+ CompletionManager manager = getActiveCompletionManager();
+ if (manager != null) {
+ manager.triggerCompletion();
}
return null;
}
@Override
public boolean isEnabled() {
- CompletionHandler handler = getActiveCompletionHandler();
- if (handler != null) {
- return !handler.hasCompletion();
+ CompletionManager manager = getActiveCompletionManager();
+ if (manager != null) {
+ return !manager.hasCompletion();
}
return false;
}
From 824873e89437699026b50d98744af18d6d452996 Mon Sep 17 00:00:00 2001
From: Sheng Chen
Date: Mon, 6 Jan 2025 11:04:25 +0800
Subject: [PATCH 052/690] fix - Reduce the spin interval to 100ms (#94)
---
.../copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
index d0a706ad..c5e939ec 100644
--- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
+++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/handlers/ShowStatusBarMenuHandler.java
@@ -202,7 +202,7 @@ public static void createMenuAction(MenuManager menuManager, String text, IHandl
private class SpinnerJob extends Job {
private static final int INITIAL_ICON_INDEX = 1;
private static final int TOTAL_SPINNER_ICONS = 8;
- private static final long COMPLETION_IN_PROGRESS_SPINNER_ROTATE_RATE_MILLIS = 200L;
+ private static final long COMPLETION_IN_PROGRESS_SPINNER_ROTATE_RATE_MILLIS = 100L;
private int currentIconIndex = INITIAL_ICON_INDEX;
private UIElement uiElement;
From bc1107553dab67f443ba04b69ab56f1415f940c9 Mon Sep 17 00:00:00 2001
From: ethanyhou <149548697+ethanyhou@users.noreply.github.com>
Date: Mon, 6 Jan 2025 15:17:49 +0800
Subject: [PATCH 053/690] Added readme for the repo. (#95)
---
README.md | 49 +++++++++++++++++++++++++++++
docs/adoBuildArtifacts.png | Bin 0 -> 35416 bytes
docs/adoDownloadZip.png | Bin 0 -> 207071 bytes
docs/eclipseFinish.png | Bin 0 -> 206083 bytes
docs/eclipseInstallNewSoftware.png | Bin 0 -> 386249 bytes
docs/eclipseInstallNext.png | Bin 0 -> 291972 bytes
docs/eclipseSelectZip.png | Bin 0 -> 404596 bytes
docs/githubCopilotIconMenu.png | Bin 0 -> 20139 bytes
8 files changed, 49 insertions(+)
create mode 100644 README.md
create mode 100644 docs/adoBuildArtifacts.png
create mode 100644 docs/adoDownloadZip.png
create mode 100644 docs/eclipseFinish.png
create mode 100644 docs/eclipseInstallNewSoftware.png
create mode 100644 docs/eclipseInstallNext.png
create mode 100644 docs/eclipseSelectZip.png
create mode 100644 docs/githubCopilotIconMenu.png
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..121f9d3a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,49 @@
+# GitHub Copilot for Eclipse
+GitHub Copilot for Eclipse is a plugin that brings the power of [GitHub Copilot](https://github.com/features/copilot) to Eclipse. It provides AI-powered code completions and suggestions for Java, Python, and other languages.
+
+## Prerequisites
+- [Eclipse IDE](https://www.eclipse.org/downloads/)
+- [Java 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) or above
+- An active [GitHub Copilot subscription](https://github.com/features/copilot)
+
+## Getting Started
+1. Find the latest release of the plugin from the [nightly build pipeline on Azure DevOps](https://mseng.visualstudio.com/VSJava/_build?definitionId=19562&_a=summary)
+
+2. Open the latest relase pipeline and select artifacts under the `Build` job:
+
+
+
+
+3. Select `GitHubCopilotForEclipse.zip` and download it:
+
+
+
+
+4. Open Eclipse and go to `Help` -> `Install New Software...`:
+
+
+
+
+5. Click `Add...` -> `Archive` and select the downloaded zip file:
+
+
+
+
+6. Select the `GitHub Copilot` plugin and deselect `Contact all update sites during install to find required software`:
+
+
+
+
+7. Click `Next` and finish the installation process:
+
+
+
+
+8. Restart Eclipse, and the GitHub Copilot plugin is located on the bottom right corner. You are ready to use GitHub Copilot for Eclipse!
+
+
+
+
+## Reporting Issues
+Please report any issues or feedback on the [GitHub Copilot for Eclipse GitHub repository issues](https://github.com/microsoft/copilot-eclipse/issues/new?template=bug_report.md).
+
diff --git a/docs/adoBuildArtifacts.png b/docs/adoBuildArtifacts.png
new file mode 100644
index 0000000000000000000000000000000000000000..9ea85e9b006d213452caa0af5a803642ebbc88c9
GIT binary patch
literal 35416
zcmd?RWl&tr*Do4E2q9RI;F6%h-7Q#vU_pZW;O;I7?jBqof(Q4(eSjbnoMB*aAKZ1$
zw$`{jJO=l#D`w=Pw?rZ#)`boc7jtAA^)-9l}RcZ0{L
z3+)vrcLc*iXSVFrnS}bb$1(OMcbL~Mb6ct!X(EYu^J3sik%+-Gd^|iz&Ea6}ywcth
z`1WSnX3V%Q744k3&R&`R(xE@E|2xayF4$v9JYFz9oV7gI%b3B#WtwNzRVGKZ)tm>>
z=`7889=^9KbukDu&%lV;%&sbMT~6O3N;(cq!i^qPyMQ*%_Do3eJTAia8s;1GZjZ%#
zAH85BPRD*3jK+__Xa}~DKnm{w@eV3nqZ;Cbp%@Vg8lA4-8u5Tk?Hyi_qYaOAt^Hf$
zpi?4nU{`Qkn!G*A?fW_kLD49>(&D77EU9F#;|>Q|8JWOWR}cEr)#j5!U{|mc^>!wQ
zz@OwC*=TTIb8qHkh$K2?Z`2Sa_;iS>e=-8AP}}i!{Kv$yt==8Sovj##^Dzm;0Ci9h
zwbzO-?YtOY$nWrf2abog_sw%^u0^98PHXgdT;bm(D)l*wIQV7ICyDru6SC_2Ew&|>
z`GAKfukH;mT+yD#YuHFt_Z`~2kba0jd0tPq+6y7at}VyF>zbAue_b@e9332%
z=2WEiM7sA-g?Cz}Dd+UTGwo`V1Kwb)e?I7aPrNiD9n_j2QX>?x0m3TMH&yk$I+E^Q
zlXC^VmDo9wkQg^UzDL`+I0ZSrS&AwuE>2EP{kF2vXR7;Q|F2fU^TaUyu3pYLRu+~0
zj)B?`VCcD11rS;xHtBp1%|@9zRku4%_v&y%hA_Tyy^QS@T=EVeYhXd@TfNAYJ+GrT
zmFlvmK==I+U6#72Ott}**v`wNz;!K<7jHf(dAlo93X!&%ZclKx+byletI(f1lbqv)
z)jK10IPV2!Q5ilDOlL}lw58;(FIR&&rgUU&6fm}Cn;Ksf%GCCJ>)(6N#g_e0jka0r
zYnInUi?<)GRoe_qyZcBI(yQZ$ukN-7D^0YQ$oO46rvFvaum@CM2z0%hs*$s*$2YsK
zWXt||`Dw0(rTFLjk1r}NCDQX#$0pp5f2d}$TnQa=qS*fC|DtIitE}+~`pp6hwAYuN
zDD}zHs%^6LGpA1$yv67(v6F!efi%>r*A{+?;5%}$IpsKsQq`RLc7oDVOH%@)y
z=63|5GCRj2391EfyRKR@>pI3l&GKQN9^1k!x9`Cj_dy=fLS@Ez3TO9mLkE1~2}8h?
zX=S68r0s?L$7v-4BLIAXkvdY3S?TnACwsF|DP^UwMrdutqK>|K*1$duoM{d7zp!+h
zFbz@K?io)iY
zjQzz$9i{(q8Q$7Ida9Zlop4qw+w7>zF{C
zKl;a@DW?YJ9~L#fyt`#I`mnV%^TLp>4ZFNwWo@QkZc2o<*^)y^W6Mn3XWz)NbFD>`
z&xX>14KF8e&c;TRN`Rf^qv51zm-jiJQ6@pr3UKmb8+oc^>AVz<~!%`Cy>b9c$
z7C#A-eJ0(9w@w{=>D!H>t`XGW1Xs)R^nBwU)u-mfl339;B6oTTiG)f`;1-5Q-u<(=k5G{5$l;~%+}Blp8(RmW0Z{Bp~~
z2kySi0HO7w-o|N&q>M~a{}m@(Xk?-hqS=^Hj`_LH(5U0c@^iZKPD)e-hWRz!%5hI_
zVyKf`0eE4-0`taMjRyjOY|?!ycf>M{?d^M`JW;BB&Az#1o|5}R!M9gOMaYFNEEiud
z+eJFY`?1^U9MbVQ`3!S+u@il{-IGB=Abn}cz?B={8Op}{q9@&C(x5Ym-i>_MYg>)*
zZV~h@1VfV(VJxu~>t|`Ij(-AVRhm)w?5N%5InF4+7y(ydqHCi~Rm$wC|NU-o_I=;l
zD;X(o^qd}rh`2}$zwop(pN`x1m6HRx6{|vll+=M2Q)j-D?N;gI_nFF;KV!ZCctD4J
zYIWbdO7&w!F5k5=HVQdoW8Q4EGM=nfK2u4+ZRwZuO+UJT!+uQoR0TOXs<1=$#ACY!
zS{~FI>voI?z8nuc^Svwp_YS#^%nCTq%I>(%m6y6*^e&HZ;wgPl
z8mKmf6fsp+%KzgG|I$|K-F!$^OAqhl$={w~#ZP{`f!))$OdO6*%H<3Tbw3Dox1CU^
zi0-|>*+1_6g)@a;$+%l)byY#FCUB;S!=lu?W8W~F#@FG!G5fIh_*V0#4Qb*g4V(wT
zH;@F5?VM?qtzBuksnQtYQl8wSeh}AFq7hxoG9wbWII%SUa`LLYJ*n9yj&SGqf#fP_
zc~*~%j`9iV+AuH`r|kJIyxsPy8RxB-cjD$dY)XeT*U_31V7*j&N5Va+8tCZjd0^)1
z>Z)XS`-bj4Jx-tiSf^UNLd
zl|zIW7`q!ty_bG$f4~0RY-8r2cM;xt36z?qXBpbu+{{?&5aqcju_S$Kk^A0;kSt8V$c&G>bTjA**^CYk8GbeobK)?mo`v1F->#?6dQ6jJFpe=E
zc+J39YRD3GDjo1bnX2Q#8%O+9@9Csh
z{WCKe#4_4^w9mEe#Ji(P8JL~j$vXNRV9JV4m4IG4+SwZ0blBmCm)+r_eKu@&eEJ}*
z?WS~`;hUH$nd3ZIwdChQEH#ejEEvVhtHFQ3@~ef2J5>MI?FC0dJsl*%;@JjW`Ocu|
z?cnH=&z!R`22=I)1U~Hq>8ujg9C={Zyu~2wN!5
zj@myOsyYEJA!0IJwOP8Xi4bDrk|D$dh?M)EDu_GWmW++*YPb*#n(E3(p?refpgRh-
zNj&jGQJme$deJ0WztKi`6y}aNtZSGM;Vxea#jSC^*M2V%u&)s4B6D@6+>qTGZrC?2
z(VfwNPPfJtlgVvIb3BGQ`BcuEA^&3Icj+-b?rHwPOAyh*SifXCNmaN4?fWc}46=dB%8&z!#nE@Uq|SvlH{tNG!T&gyHur7pF7&-qs75H~
zNFlI8C35klff2q&ht3bK8~
zpz7OCTH3x;uFTSXl6^Rsa*CL1Un?NB7M&P)zYwwWFwb?D;5ydW3f%4oaAz!3Qtz|K
z8bh^MSzLoU?PIWrl%VMwbjm=kd8~xPY4p{B>_C!q!#rt0v!2%>
z=NjU9598m@+5${;NwCr^Ce_tGe+9UsqijXci=OJjthrv$n&Ptqv#D_v%(YSbrg0^H
z^y7%69;tlL2gVlj?7WF(dtrj529+zF=m%cy6q?P0hIiJDDW!kT$4zUD5U0#f;@7rR
zd-y!+q>)Fs5y;vJsC_LwM|z(3b4Hkc&x)|tW}wEK4H!W?J8~CvlgC5Ho@Vx_I2;%-
z&hZAsz}pRujVK|$(5_pYr5G$vUa1cxbbqE~002~vrtvCNw#sqiinOpTcgM#KWjcB5
zC&I21;X3%7p6pPMqEJKnauz@I2gsBW`yUOX=acgHL6PM~eLt}kGdC_)<3F7enWs#)
zd<)Mavt87+B_|u+6(x*~3k!3|88cRE^ziG9z#X#FB>_15JdOqK`cnA^yjB#Lo%%yT
zXOH*-2pOL@Rvy5^CWxYQKEmifysxJEb-Lb(tPb)=yu&0u^HxNRa@N^^>IOKmjEv*
z!0b4@a~%IJZ#WJdG&scscH7Y{961qm5$XZ;?bW>!7~iZRHkjbC>-XEkoM0fs`7^cf
z(P7V)$e%AsJ3lZu;`jk0J>QEP9akikr^Y|$wtS>fU^FjE=7Vv2TG`fuUUo*n42kRb
z#4#*srj-%nX@GK0^ixbs88g%KQp^ZOl%f5ohwJ@<3s%;m+D?lec@XRg4a10-B*Xz)
zsoUUta3!33NuZ*n+=sp(c*6Mh{D`uYr}@}>qWzM(L%@sj2R4e-fGkObI>m<$Cqh}J$wsI8w4p42&LO0GI&5E|v3pV|Ei10+lHXv;G
zba02YL)TZ@;#UlA-M*PC6zOujwwy?r)ndFPI$QNX6qw_7FT_&5*KEkRMqCiYlTN0v
zKZ2a$x;y7VUJ}E`hnA>NY1PluFP6VeG73I$CJ8W}`umK=y
ztOO2hW>_HC^|PCkTh9*f<*Lp`)(Ve1QaPyOk0XFLywFdm%}qFJVop2`rzO8A^!0Yv
z*)wfQzDP+%aYZxk_!```
zm9Kvt{AQ|(WSi6@z^dA*1uS-eo1sZ`@g3JpN8Lw+;hPMr$|^s7X009$?+QtUy?GfQ
zS5o}z8>3AG@033hBBg?Q&x@1_n_z7I!T#0
zKjO+kAiLkR`#~UI#Xm(ZNHn^d35Oz&PMva=)$g$-$8U4ez#DwPiaY3pT@@MW@70x3
zi~iA7{E@vQ1aUz0&RT!Fw7h0o#FV)rkXL!ws$+5Y@B-|6y0oCTApA`E(ujqZZO
zSmx_ZAIY?8ZnBuDyLtfv6p}mJChbl1`nHqSH8hrtdeH)JUNyow;Z4ic80aS#l;F3B
z91xt;ErZ96c(k&0ELS`?(&b15#`rHS@r$Y-o$+tpUinPhh?6Z+`~>)GjG9x_k1OzLP$YXtBmDqHH|+#YTX
zJ5^LC|6*(M%Wc@(yekRX<)4ah>qR~0d_1Vhyf}smdRSZKfE0cTM6gtqsssLctg0NLgg+Bk*bB9`Xye$}E4=4}&gR?6c}a3J
zj=Ai`)g|%CKa&^C+QED8y>2p>PM6$?ziJZ>Qm8?M6peGoz?3x|CT0kl~)~SEbW|}2bug-
zgM&Z|e24miKguURyoWMWc;P-AQ7S)c$Ye}`4IyF~VUNMMPww;)7P3+5W2vnv-fR7+
zh=-`i;cFItoB$CIo?GuJEQT7m{H1j0$5C6Nap(0VZl4Ua?Dn!IFn5OatBU1FBJ;s@
zGG>0+x*Yx_Yg}JpJmZrKmIV!}}
z7qr0B0&+n!dE!@Ly%xP~X3WgCo)GuWpavTocC)zwW(pMn_6mu|_JOoI!OcJY&cG$0
z;g5&=DpFqFP&3RX09ON1wHb`e`2zmQvBv1a`dCw#LZ_*T*Iy`UukK)x%W3LGUlQkJ
zSqcD$Og^}#)60+?d1xYSD?QI{uwv`Y!^Q`!@K=QC>P_~u&FAV!7~`+#M|Aa|GS?L#
zF`pCoTHw*{WwD6jTMxUNeW&gXV~@ZN9MopimLDUgv?31r6U}ukGd)5Sf|gb0%5+mI
zCS3aH{*6~rzlsZ5GfOt)lPD_1Vc8RWnQdcd{SLEnvTso7w58I{S
z`jTY|=)g`33`8EIt#;tr5K@V*MIDY!J+}MA_M+dhBk#4PvhNQG@v~X>uAXej&0NjR
zMydmK?$bJf|8mD6KwlSUiK!fD=*S9fWUz^dbD&&(?4r5kym_$vI
zVxtBxiwP}KhuzN;JZwdc*e{+Pl0XLF#Ev_}5?xU{bG|1)fXHtrOQ916?Cl{5O38O-
zEpxB+)cP8ZH$wZXx7V)I)eWE@H54g}8;m!P8WPsiImQ8B`?uQz1{|GO7?*KHa%khp
z61vOLpj)76V1{ZQFl~dqGh*Iy9)Ce^?vFuN@CbNmJPaLodjTp@L|4MZ-MGq;Bj8IR
z;d7&rHa}DpUb6LY-J;6raq9b%O2AX_a4u3_?E8ZHQz^T!4K?@9C*9=Jqi(4*r2$K=oea8gc9M)KZpY-CIZDGe(*NbC8q!
z-M8lcX0b6=^|&iTM0`ck&GEK<$$uMl%mIr4hnH)6B3n?9h14rsVEC-t@ONB7$M4|Z
z&8qf=mZU6N9A=qYJwKpPn|)Hk+<<=~BZuR4EK75@J>T<21TDD=bo<%9{!Yh1M5K~V
zoWBxd<>ryjrAE_B^!z)g+UWQG1sNI~(N?R|1P-dLO%<3!zq&gUE``6Ss^crXzVBgi
zTM7((BN0&Bf~g?(1c^sX73wU^gQtFupr==e$NCpRKgj_(H~7i&pU>9npAi(dmOO8_
z6L=kwz_4~poC){ZcvU>*aq>g-agYrQ`cMy&!I&Fc@7+k)1|O)Ov{%iIxSu8_Cd&j5
zMfE=yq4=k@I%f*$HThb+%~vt%tN)5ZvGy_<{6rAc`N*uoYENLIoN;$sb9Uc;yt<}J
zzMHy_f|@oer>>6QH4a4!p^SR-ns<6(A@Y>o?TW|lW;AKV-gZh^47gcLWUtE1q#$lE
z!M`?(Gp(sD=O!2()%;~yFtOt4PR_C0o#M8da%l2V;V?R?<8(~gpGqwGeYuBbm2QvK
zbeg)FhGGm)W{w-8_aY*Jv2M8$ok6EJq9w0~ucoq;3;te+FSAG-Aao6v$ATH+(*oB0A;ikS}bAjVyC8_+Kw2jY+E6W
zhTGEoKT-Ho4CT3gcy!fJaOTyhJ8?4#a(kZ7rl)Esi%$ziyzTZo^Tjzkgg9~I;oX`-
z8)~hzPUoDQwgrNGBYwma;)}8ysG#GR{c4ZxCCUE6`Ee#!YR#tQY}YW|S-z#wlR+r{
z&&dQfh5u{?p&TVpjGclc)`2sL;TKQ!`mMI(Rt{dPVBvV>-OBQk^Gbt%m$;N(FnY|~
z9xo?E-MI_E`#;>Qqux?SZH)*imxgmpDGkJO#`aFNukS4sC!A-5XW1
z&3YD)+WE;gC4T1`{1}Is9SCmf_RF@ZkOtDk9Y%*3|
z4m%t`CgkKI6b$zIK))PNp_BNKg;+P!*&sF~2ce!(Ya=${a8KWi;~9mI&8wcmnRjs9
zoo#H)n>B%vCsx_@cr?T6kK8T3RJa_wO5rsgKjEP8Pf|zjZMj!(oWxy;!pcQzc3#gr
znA`;&t!m?{n8-?#SebF#FpbIocwKU#f1VDpFa>N{RcBGI{5|R0{n95Szsw50!d<2ENAxuXu>}W8%Aj
zuwc3u!$}5LJ2tFUcFQ#bbP`mhsObDjA5ok5W;I_l2<~-H5-urs2>QP
zlxS1Q%6~vS&YS;m4c=~6RN;=mV&lxzjRNhQXTRuPxgkhVHcJ<`Tus`^)Ta)d^yhc_
z0r@g%(pQ&l%ksr^&i}Hq^)W|UIbTJAfveZ)LA*v`Y)s&hJ@B`ZA=CsuE?cPlwZwe;
zV0-dFm8-waGE*jh-djvOWv0AF=K4bcP8x3yW#uy78`dl71%q!M12i!8rtKtwoeYn+Y;}WPAEv%I
z^0Q`H=XG5_cbiF+O8Jpt(m|DQ3N!>|r`g?6xfHZ+IUQ$~D}Gk?m{x&BtzB9!HfU#w
za%vrYPg}KRGZctwba#N$2BshA>GR?D)APXix_MCVyO4&t0BSrc8_|4(LGRh
zmg0^IQkNa}P2umbzrIb?M*0XXp4KfnM#(&`!y_uacCr|}n(7@Tv6ktmqn_(mR)3nb
zah{rMAmPjE{Uyq8XIes|9P`}gw-nwrX;2C6RiTP88;d8zP(9@64yxo72%oY0=SVEO
zqRDx!C^XnKW@eosox*I4Py8!eef71hjE)Hj1C8$7Q|hNJP)(axbJ`k)`ijzS-%hqs
zm`#wF(VJj;czBFPtUy}MyBmN`Ue2^4Z{yH-+3bNM(6#Aa?^dq>>w4`?Y@cN}T2^LT
zB=lxccz4JZc*0PUoHQF1OWB0(WGclwpVkm4&4-tO8)S*nS^xMk@@|Uew`~^2Vm3H`
zI!>aPpjBuOzJCH`yVTVBsy@FUC1}gvjZPsbhEY^xk@zDi*ozp7^wRgrI*G0Vy~%Fi
z#8xgty=pL~#&e6So
z&Y}HG@=-p|@3tot8t<#BhONiZh
z7QSop5GEe2$CyjkXf)$eRMhX;0y&_Sv$^pcS>d{_9PV)POSC>>1R*^$nJ&iHe)?7FC?B~rpp!Q#o
zjW_R0Yg@vi@XK|_?m2?`9tPbq#O(MRqJNduIP_-B*_hi$Bou{ep1s7eSdxt3szrQ>
z7vFpJ(#mF0A1OVE1QWceu+u@~>QV61btbFM)#I4}WkObaF8Q{|2Z8xs4i*^RfiUDuRmKMJL`))1N
z)JaMN@dj=EQAz*m0yeTMJe{8LcyJuQt$s^Cqt!|`kg{a@Bm3n{=^I2X9=
zh>wpiuc87vxIAk5Sc(grY16P
z5jlTA&%={~Oo=Yz_@l$Emp;Dx=-|Ni{o}Wb3-|hl2AB5by1~EQ;e4PJ=X
z%)7h01v5&|kKvt2VLku;OrHI;D5u_gE~EpY&Qv>r(mp<`s+a29N~%wKjvzPP<@
z*|)|S6&2+zN`>}!??;KGSs)1}3i9&r2wzW5P2uYoN#hLH62A_M{8Z@OX=K2z4Y&Q<
zJ8;#r{nT4jh6b;1%^ALrBlND{h?i`z*};H4-S!eWAv#Chv@qx)sM+D?r1k%P0hWe=
z&gWDAcQ6fXx>kx;5K46O1&v{(sZOF!78?;q-u*S(N%3m`K?dc2k9oPK{j`t&M1
zq%kzh<~9E$iS<9G=TjusMM4WQ?`EVv;M3U30R;XY^i!s3T%ZAQU^fdh^UtoXyx`#H
z$tfuS?c?;Ho~dPa+dDUoBX4^y|Wt_sxD~G23AA;zx(pvSM}*$wf=V!8a*h*SD!J
zlqcp1bvg^@04dm6N28Y;|BA7$txY&OCPqP3m63yk!}F4z34RD@uCu{us;zeyn$`*!
zjGo$hL5q@;M!;HYjqQX?PhM$~U^P30pp|3T#P0GZ*LC7BaPZ-#YZM?!s+$o~rcM&r
zepTa5YD8~2Hv7?z8>(H|s7lfK(c7H>qfF7C3S$og-Z0{bg^d*{QKW6?J1PEEUe?!s
z_cjYG)lBP;x5p=!;bFDpC{A**T*y$O3Yq8Yh)c2OHKXMEe-(XwI4@ib`_BS;7p(SM
zP|z*!`TTf{VUxDZPdr%0tuTkuEiU%=@P%2V;Ar%b)3KP=Qr?;+}=LU%@5hiPCJ>)5h$~(j$@;F#@i5h
zrYL&;0QGMzNizAX^?y0LEyVVfu$}?>{QB~e7ZJ0I?)QduteB-+=5za`{!OmjU7GefDrGR_FQg&NR1(Xjwu5aJXbr^+3T{3U-J
zGY~@nL07oROI%XWH<%a@zK2%n6wQXTb@j&|;D>1W98a-LZ|!Qk3duTk^t0S09`68i
z+EodLoe5*B-}`a5RwoNd#VoM^i~64u%w~YJq(PvB+e_&hE;s3Sp0xL{GcRwON$##{
zTbe&JN^W~Ej1cxX#>7tgtYr2@)juW
z146=JHuNw1*NgD#>d(XFxsFD2av9~LQ7{Y%!AqHI`7zs!CiZTF?NE^vMHovSh&
zYV#1$nX9pS%qnKhza*^@yVS21-1{)mdj8eYK6-ghJQb@tGH&`A4!S20&-f>?S6}lW
zD>4|k*SF{HXhb1+)wkD3q2IhSu;-5SDEt$gkSlkKqP_q{O|lLxXJOEQ=(Kul9(I|;
zzNf&z!$Pl9zMqvsS}zyhj80F63KlKE!u&L&uydOMu$dhPZi2}+uFT6PFg-J)r1(yT
zjx5YN@}o50R<;(`?TUcg^l|=XVbi@Yf`3#fy*<$9b-(LBZSDjL5l_j~`NDbc%j?jP
ze#UxAAB;*zt1Yztjysm?o^F?P+IO_knRl!=V94!mxIzAc!88`KJ!h-0lO`*OZ)tOC
z_KUo0bh4Gd;3HA#(zTP3_dx*6Z^sbkhv3V*J4eQ@r
zS0mV}oZNBVubmeBiBL0extaCO$)kKJo-o>^lvadi8=vhh3t2yI3igE0?t5XC3(>V`Zql?*-*REgmh&
zAF&uZFbsDaxj*kKI`h>7-;q7PB-AEGF2S{L6)f$Y6?uOqEqJ@OOD-`jLnT}+7Fv!2TDgWuNHQMt-T_hykVu{}vx
z?SH=KxP@f8AJrd1!PN%8Q-dCpN2_m`=ASMY4IV32oLBtFo0-nBL+V&=E}rsTmYe3C
z$NA<@QYwyC5BZc|G?Vt%Ct=_&55{R5j;q#mW>Fj8J*yelle1G=kuKo2d^+YjYTbHS
z-MURy-Fc-~-MU6ZbpzHWbbz6w|4~6hy7t3fpG=(uzRslMMTcMME!a6UH`TGmq=e`7
zsiDR%vhVVtWqUh-Mva)T1!OB-{F3|CrQXGqu+B%9E
z8)!?u&C!bc}hUARYZ$qRa3AqFb5~c_IDgJ??>^nH|2Iw-ugaOVR^DCEv9378bZgaB)y-}m}
zQ8F|#(%72SS)hm-u|aCbz+l`tK0K4-F$gj&S}#I)<*A_v3@htApDY5A&`ffUbP;~?
zq#m!Wrafu(52Yz^_$_nzfjuIo5FJ@;fSLeBu3h8IIS4+V*=i}=*a`r}Bkc(%#RCqy
zwV<=#LFUbMd+T9~m&?52->Z8|panMU;Dz{(OxtF~ZJ{K$$8O~u9ItyJyci-p6{DAj
zpTLeKrh6<)E-TX=2oIjFq8FlC84OmcINdi4M;$vJM>h~53WGm=(YRDQvzf}g
zVF1*!ze*;DTp8Iz>-IuZX!N2QcQUt72LdmL|4d1R+=pQZOE`XPlT|9|i(OnE<>1Dd
z>%g}{A>ySw%n9NYFj1qK{YE_tk=8U$Dq{P?QYXZp7;XVb5+wk6xsV}&_Gt1Jg0iirSj#L7e?b|LbdaTc!SUm5LRbDdV_{h|GI$7qxX4F1*WyaG8+wJEUd
zchB<+KzlML26?V}UT#!Ek2fb+RLs4tB=pI0mUSOVf?yoL!wG60hQSDE85jxC{e!xo
zyl>?2MKJ^^MfO$aev?QibNFO03fDkKB|CA~B2na0anWAP4&Pb-C3I_u=f>a&Sc|~1
zV`>;e~mm*yaG)a?-xpq0#oYA&hF;~Y!c=@O;&n;CBDMOO$oHcT9!x_Nig
z3i(|m2$~Kn(A_pyO;$Mm+U~q{I^U}2aBdf+bZlGhBayD>AVq@1c3bMYs23U}h-s15
z;{@E(6Kd-1wFs(hT->KT^rO}k_zYX<%8`PtRvDS5}X^??n*V>TMB)X5Au_
z;l7`@!T=&-CETUjC;a=2|wo?223EBc*&)0&lGaKD$sM4?X!#ddP0V#Q@3Q|fA
zYh3_SC0@(WitNT53g0-P%+Vkh-0tp!A&O~6Ol{6+M*675TjLXmUw&POPd4LWi&Vk8
zEqZbnJ)Bqz=k{EzgLGbpv`&$uxiuTHx>@R4e(h^dMUtG7g%lmV=d3tDmk3_uq_;!MzA*+E
znp}QMXWxG6GhbKY&2=u|&+`)kUk&WOM}>x`{SnZB>7<3Vqvq%B&Rs#5MbiHstiMo=
zwWspfGYu8a==--5>uFUYV&r+pVrQqlonRuGGwDM_DF6JbOHSy{>)DBsST6j>Di#H^
zt%x`g?zG(fDE%@lEYp3YAxfrq@8XNvS4D+A0k7Ly3Ka?w;LdS8eA{%9t&U?hX&whX
zi>o=VDWp9Y>AS$~Ui-B(LDH2Zx2x^0pCL&c&0J%K0@*D)T?#%*s}*G&J1Mf%r+f3%
z8`n2pBD*Lu%Be`7qs^Rf)$_=Dx97UwRoKNoa7Z^zR?#gH6rNUfKuJX%#gs_`Xqm%r
zCR%C>)PD((#o%-5qJ&JS_ENbCQUJxOBxn9I?`C+9+J;O=?v!O{CdGtH{|W)=`QTz5
z3da%#F{?G4kNkj$evRP@_c_=;xf@G|=rrZsrTHSYWee#Oog)X8vZ}nW@#Rf?DCr-c
zce*3GbroG!mzB^j$~IxBrG-t#rh0nJnYmNlOzW44sX4I@SUN;nbkT+@O6?7eB+k6m
z=1-ux6N@W&?O`%><2{E&`tCM#CJgnxWnkC0kM_OIeFwL;l8GOktqQ)%$e6GEg}nji
zcPZIf5&ioM){00^C~U9~$)?}g39{;YxV|~o26Nj$20HOC>;W4te!KHNZ}t>2>ng6x
z4IdxbEK)Lrt%U3j41u}f@(a>|D(qtI&8>)<(U9qMnP3M_9CR%JWn4o^EwEF@BJ4xZ
zHNAKw*-!Yrh$av}WmnjlM4{l74iUp2sjUO>x#x%gUkdcKy7-=g(Op{-sJ0kd{9h+H
zt{|lf&Yl}dZnB8SZakU2BhCd-6zO)I4^Dv>H1(rb+7AyWJtMfnk&tJhA^
zKzYuF*l0HH{tchaF2i+d-KkS9av`uI>KY6Xkm%0}f7U!*cYKZ9kml%r-JHZ~X{NEg
zkkLKcL%TUioCt!1(T35eOp)Ks4#@!{6f*~JxVtv=#A=-d3sYHhhbs3PGK2zzr>(}!
z_N|UjLEXn2R#y4FH3*&*k0f(Z1|`A$!jsr(!P|>|o9xSXDN9teq!NA7AFPpiCEj$W
z&2e(@8sh1JKgDX?{BCB%|6<9|z++$bp+Yp@y(Y}!(3|iCmh$lvOwGVuR=cD34ZjLS
z(g$Vgr{uzkwy;u(oUx0&=}hqeBuW3Ce?sKW_DMfY()Sm6%SQWx1k&_fAx$5e9JXk5
zUhZoye+i9<1<*^(cv5#!G+jf0b`Uy+_ru!`j|179fCn{-0C*jR-Yem9h3MAA+<4CjhWVHeBxR}K9#uf7TR3`E{PD{do^0C{>V5uv-ZU}jm
zo)oTh3<`TOo~hiSw&Z6jwXHBLp01$$-Jw7FU`Bt0I=|7rq>v8uCAOcg+g|Fl7~TED
z6}BM=ynavTebu1=1n9{X1vY#eJz#v#l{CN>vl0P5=ta7a%JMW!#Uc@gmsWX?JGlnD
zzR;1Pmd|j|36IlGlyN*AS8v3;O%o2t`CeXLiB5<$-G+}uLL`y_Me;ii4ecb~%5f!xXVC23~1
zF$YiFi*#Dc+Hg$c&&+$7ndau^$v=L4{yEgNOun*qm6Vz#t7VCNQ@5y?Zo{D^Y`HJn
zi}gB4I6@%Ei(U?EUtuDxTcDjh4E-@uCHhU0vzp`Zw+qooZQt7KL8XV2O1
zLE_+*_oM#A<>nm!&oJ0^X;g?QXn)V2^VpD?oW)ruUX02Vn%7)f>E$R9DEjpK(f+vw
z5@|s6_xJZdup=|_V!0h19SgJVg!;Yj{jBZR1SiP7PzmBWX3LrOWBOKX5f*POB2K0Y#SRj00ilZtH(EhULc=7?lT*uhs`arY#M^Is?is
zhsmC=J!285GTHylRQbX1(>weg8wcq9N`2*_?V~*aV49Sb0g7~_C7r*2#aL9)-kV#;hQi{AhF_yNDKp7nuBqWp=sUc+@a
z3lf(H2_v+;M#fw}Y$7vj56_t3l+R3S*Z^3~b^63mM=u)7(Ryc{srHT`N&Rn7
z&sMcRPkq2^tezDaG;=H^uYtyZ5BrZ#of?o>QIs8*y;M>1UlDNYqx!2-Zpl}mpRl0cQ_Uk#YZ+5y-e$P&W;;SK|NV&L!
z44zw5Bi}u?693LTJ(1>b$dd2SKG@R)No|Gh8RWj>8Yaie9D~11Z0yS*1AyRnJ)979
zJ=mgT2fndc?=gKoo64Fs9IDE|18_l`TzGz3)|x@@#=x}}0?uf!4-F4E?IK2o(Q1*!
zS697{hH7efmzS4>goKI6+(Wx>_oU_pA7)H&ktg}^H_;$u*q*Cd*?CJ}^a>1K_+y{s
zx30Y8FQxsE*O@bY==+%{aCjv@=QWzN;0CcaCpTAfqQ9}yIJ0HY2}fSuR_XCV1MnzV
z(i?y|nEjiy<81MTc>erYqy~5^HRaPZgj8RC^BEvmleo(C@J@M#7d?suGh%BXH&wy$
zTIz1Qv{4@u0RQkdM%?s?0Uh+sVld4*g7STa-f$U;*s$W|K=pFBELr(tZRnmNvooVr
zBGd^>+O@g+8Hvc}^EMkLUC4`Xvq#^#WN4|{63C=YOJzh4-$KB%v$JXj21>rZ!ahDe
zbroI;rcTC@bG4=;G?vN_vE@yWSfNwV3GyGL`oE-O9S97bB&Y)J6r=S;H*?&@1gPf?
zO^?w(nW~zq;<$&GC|BrqIpdtCSDDT3(=d}gmqCJoCGTn1y}1gBeso;3)m=&V_z}xV
z=R`Eb6O-zg`w*60#b)24qzwz{)Wv`V0FL=m(
zJ2EE~-21M)yxhAJn*VUDn)u5@QJ2>aKR-Y?)2D*PMuDX$2oZQOy4;y1hUsjvogALh
z7>q*UJIv{toFnLYBe|l?ATJGJwgf|7l?z>v3*P%??XAeFD&@O1-Squg;t(l8)mG%+?tF
zPe4PgueYZBcE5zRskM{)nqjLgvS^75`;-+NUgEAsjI
zeP7m;R8Zrx*pg+<70Yd9VSWX~S8xuunV=dxWy}ZdNlydvn}gKGVSx@S^KeKi7!k}N
zjtH)Szt3^`eM&LQ!Por93;9L(+I@uMxnpBOvfk*_0~vUodyQM@9z|(W#SIxZ5KYU<
z%HC{P1ibCycrrym3ZeC=K?p8+VX@Jl-##>@
z``F2Tqe}=ADP)mW>CTIZus24D*Y4PWDGXDy5rPs6)J)t%M-ILzP)1`C(=PWK`-W1y4f9t1eB6G3?rk(cc?DtXberBQqI0^P5!+vOcuiLm`|P1uq+ntjQW;-Fg4Zjr~mmm
zGqYWpQ;!AwLZ{KeEuKWUvv!>H3p(MdRvqc
zIPI;`Ke5S~AewbP66CMe>4kcipIqW*>P;_wqZKQnZc^k=+ME~IW>SBA(bW9xr)evWMVYTQ7=!f1*$IkhvmWCz6oL2w)ebGxW#;|iG|`_G7l5v$
z2fyuXTuYOGY=ZDAi+^^+w*Aw)r)qZ3?$6KGWI;$wtkJgRtvNeI*yzl7Cvx%hicjl{
zo@U4G<5x-9uh^F;n8rV;y~;udW3*~Zc%{1T;3K#nf#o@0r!DL9%!Mz-B18Nb28kd{
z)3x7PjPob5Tw|U}|5s~o85PG9hI>MA*WeN)xH}9M+$DtII%sfr2m}l6?(Xgm6CgMQ
zcXxM(?f<>IUsgWs-JNro!#UH{UG;W%b#>MA{+^8C-cBdkh*p)r;}N>mnxxxVqYLV|
zokm37@2YLJp>c|)2^!y3rwInGXtvy%J0BWV^y>U0`_t*FD+=oB(w?9m;&3c| fbW2CmwWNu2641Ey_U`p*sX`5lXS|W|Ks>ye$NE3{l{eqAQPuidIfjkOb-G89w
z7uUB$HSMorhrrnc&U!^zQ4@RM>BYwVfG(&Td{o~|?03^;UT?Hj-tqjl&m;p1j8|Ta
z7xv<*NinZ}266UL1na)?iIAN^npb&!LYMw2;#TX7uB38mHq-^C{fvz4<-?X4J08k0
zf)KJhLkV4hV%zj4*2jGA-Y!2nlMWM#bZ5LpGHP+y31lm#^{0hFsREhEPlw;w0)Isd
zN94kIevR2$I`wn_sYsh%YJX?Z`P0#gqN(*w5j@k^=MRaO_+E`g;&4SsGS7P#fS>bY
zn~0}MXP|=BytbevZXmQ5NSP4Pf`cJ+oRkT^KUJ)LnQ`qi=AX{;^5>-;?Q!Z;&+ytJ
zxEe9PcuZDH?J7uV6U8kS2^xPLaC~^UL_JJaE&_=;Ei&MZrwyCO1B*cdp{Mp6H)c~#
zJDVgRUp^R5v+d7j3Azxjx2K+RCME~Ro3UnlG2Zmx+3N@6t#5l(3ld4tl4VMV4!I5r>)G4vZ>dV
zCT)&r<9+B^zZ}0}1(oSpq#SYfw54v|rF`-ItbcO#qrQL*d*M)fjZ{sFrp_G?ufPAoY5S4O!IU}mmTKV~;)XXlM
zZKhIZG%V4I{`%B%j7*eUzFSwJa9Z~5Vw)iDc&Q1d%rf`KwzshNp~u>zH*|U)y~&b8O^3OJ$S4SU`eTep=;%Y%lvxT^h(C_g=qLYhm{i6cR4)0Hrl$QO23QY
zJ~qPE$DQ3>Kt7@PJ?>Z06*p5BXNWpYxISDfwFe35vHU-&meSxxPMV87tLp2i`HvFV
z0zfL|&v6g_ogHUN#zjTy8HJ!?gpfPgUPVjIVktGy}IQt!C
z!|J}N$du6;wiJ_12Jytsc!sa`ZcAYJ_xC(b+SZn(HFbzjT)je%D?W&r*xuZwSl;Zz
zS<}8dKDXC^bynF%rTN2spyZ6l5=rujQbAkVLX8EU0dpxuu9F)w2aY|632|2SJ8^G%
z!k02mIcfsx16f+$ZWOpor=jsPi7!Tep0_7*Nf2@H3v9>#uC-d$mRw0FQM1){sV7n8U(P_^1qw#%qB
z9iwpGXHsU?ITk;mSSZ|yq9?E)o1Q7DU6-5dEF`}_UPSGH`Kz6F+tvCF>(OQrx-407
zZyU&&;WDD$juwvkbwxUpR1F@A*qtdfIm6B#R
zd^l_F1ej~X@dBNw*B!)rBrK`cg^-PGWY|FTYrK(Jg>K2N<@%`iZj5)>iyf8-i|tQx
z!Gs`JCh#CyUO1~*O|E}h7+S^hx9Fh*Nu#RwbOWFNM&v)tCYUKt0#$bBN6U}ga7?f3;C>y84D=c(g>R8@xt0uceO?fqn&a
zLHG|AIQgF=zE`bjSIR
z`39n`UUdf1&gI)?Ma7*^B27n9bawLOjd<(gg>2Hg(&@E20@aDjkb=tS8KUF(4hUGf
zPDrAJ-2ZD)p^jMKaGWtinK-OmSP2>vcm9k*=2#Y}MC3uj-*4eL3Wh^XDF3**_
zoXFFDVUkJrdpa14!RV}E_ZysEOL`KU7dW)yOR@c$hq==~tZnUyiN9+F%aJp4TwgWn
zvaZbV>Giy(KDUkq*_ayvW}vm8rjengvk6HQ|4oh>O$&gm2rW0<8l5oLv(=$%;4Kq?3YPi)x}kHDUIRgSC9KRso#@EV<^D
z9{uZfnG9Nk6`75Whvk`lpKK}`$eV)FDZLSVP+;jg;6kTYplC2cV>BBxbYW|PTkoS3`=rwU
zG)$uOdU%`RCc|P=yyd)yzCB~bYVPeznFWh5BAhLz>U5zKA<%?Wk=lFG8m>h+oU>fB
z&G#G^&gbAxCTk-S^rmz};K^1Tb=L6+EzYGm7>BVAPUVdKGx{aNbCo+oxM`#bx@Z13
z#h~7R%YH=eC(e%(ZOV_14oK?$r-M%XBr2*ODg6Fwjtkg01AsUjdIBfd;w-i>)bgyO
zv_fz0##A!tjIqMOn>=pM4^^n1Bu*6j7*~eG2zT?>TEwpP8bc(R0ggno-I8A3&z
zLpD*x-OEP0&X4RjCkau0r_@DNRYk%pcW{)jR6GN9H3|v#Y?EfIc&cgPNp*(*@GGwH
zCf#O`bUim!Z{)3jbOYcww}@o|HeZ&IVab6$7;PDp2~icozypv8As
z`KkM2gC5dtNK*Xv;H^Y59Z4;X>-7x7;Mns+moxMJ%>m^0awmM&{_acm0Lk{aN|ddz
zIO#Jg&VqR0lPMZm!_8F%hNW<{2PS!@cU(f~WMo$yvh|@t;MBPnGU3^7DxCNlo_P~#
zx?ZqIT#qbC5}*~)6q&ML;1c0N9LINQ|GCLiq2LX{NjpwG{<_%gv0*jtZNrR>^24(J
zXi0CjmS8#h(3W(D5NW!y`ODHLb-{2B9fnJiy|uEEIU;*wNWw*({v-9nBMLL%xcU3Sa08g8T5{^1im
z+DSyY5taJK@xGUnIKIh->(B%gO@s24iz`-Buo9uj$$a`$%&II9oy`-
zr*&3~-#P*w8FhE=4u(p$rMz*xm+LZcBS0B~ps?ANpVi^rsc5}xT#=>yn8=TWzI(rK
zmZ~D%!aqo%p2|(=B4D9eCi89G6vw2l{~{A=o2t~|ZF4!6OYZo#!d#wmHDJ|o_a+!-
zMGW#)?8nsQiX~KMp3&;$w^l)?e{*H!SZ7nE{7%;Cbz@|>e>y@#Z9ZiugEu9EYp2a4
z6eajuxRzWUZ0}4i=0^+A@;;~ksO{c0pGBXoI~qY!NNWn+>${8~b7Fh#RxC^erX8IS
zMOq(eY&$NcYC;SG=#sUY?55A0Cy*cMGO`4%5=Vq_qdgPP~RFCnZedTDC{CWRM{w
zjr5^&O|IZB4RNs|@C0?j=t71QXM)oZJ%Tw3tT;g3uSUrHtsc(jKc;k7Wn3&R6`+1p
zCSEbrgJYJE=G3&bMofgg}bVGq%Se)!I2f!jNo6g#cs*oPp<2S=ys-*Mq=d}
zHN)F|yGLWWFmAV;jQ;vC92cKG?5o#$ii*sP>*E0g1U+qhMTILB3RyHjx1>{*=IBc3
z!fl!d_te$=bCPLds<9?HT^sm|8L5d%qA6o6=0&{7#2l@Z%DIm5&IRcF~B{G%@r
zhEcP2_}7T#RJ{{rV`C#olc@G9z>fWrQxzq{Ri_8FMmVh^u6QWL)Amos!8~5_mZftgk-bfJW5V)%-vH%Bg$j%P@xO*L(I1_u0-B
zpM9GZ*Ja@Excaewf@L=Xf)KY~VSHKqzR4QhJ-$+BKPa5-N>y)OMg5PYN5~%bmE+~)
z%8YP|ZYn~lV9#XwZ1PKT$e$U7JXHi?Wn>u7Ca2XpZ5>|6IGy7SYm$^xR$mYvTAk(H
zv$udLnUyyczwt{h$>P4w?N{s0HvuLRTVC5_l>4OP-_x?j#n2I;{o)npUDcamIZF20
z4AyRKDG!^PF^4hgYI*offyWYkzQ{uZMX;oYP$V-2rGx9D>H?@|R$T)e(w9Q^XyVzq
zJTKTUqa;hs%=i&R&<7h3*sNt$!^CT=fw$P+<`DpiM6ur}=qPG}7)?}FeNr(9a&V*M
zW-o{2rz>#D+33Nsj091&qVS*UdKrJ-b?HC73|JNGiL&M0&t)%iv2D%VQN(Gi0Wzn$
z(_PK8>X|M)Eh``GG8de@;FnEe6ZihqPvh(WTG_2860Szr5}PB@v2+`pN~@ecQ=<@=
z(Wm*-6_5Q)kmlLOIgXVDBFO&xtSa$ak`e2In3|f9upMVV#(%q7kL)L7IFRr1cslhCO{f{*>%n?8AnAe1`hp102?Eb6(w^mJ_$-z^vsefJ)Ij^D2Ci85U&>A0@Df-zTbs&olEJeaVj(eLHFNRV
z=Fu}jM&}Qt_yr%cZ)0>iqwz2z9(^%$Y*1&RNZ2?AE#?sSv?SEz2(@@6dEmNf?|kE|
zt;swFd-TJ?Z)rTruLTu8WP2I4wOQLd9su;n$}JR$7=}Cwo$O%WMvgV(_m$;UvB=Qv
zMITKHyXDA)Xp)K)RpKw&IVtG4P1lh;#7)b{FJH!`Wr&Iekaw^Dvf`B#bf_e&3}y
zyOR1X5Jo~MpFnd1(wuKN_dP_|{W*+6Tlg1-aC+~{tWg`OHv!e)4HfsKn7sV*XHhoX
z-X+!GU_@BE>g-t9nB{sfJuG=-&EZf5<==lcw&T$#g
za{JvA-Q=&N7%!J19U{%4N%jrf^XISMq^JE)ikkvIlz*0?Zad~JV21}Ur~ILQIn*+-
zuAlp%XSYObf(cnzNFOmRpxHjyz^s^ibrIzfqbB-Yss1^xXRXrpE;6lA>2!8DQ#h
zx@Wo9m%OLY_ywkXR(4l#+E-I{{SnAE-k#4k_d|+oaPF8!`oaQvv(qyd97nq(4?gne
zw05{(qa2t^BQ0W;mAN_uahWou=!v&{7W>x=IgHy_H$5J|XYEUC9ii7Yh~ykn+|9W6
zxO+2j8mVk{Jmwkc6udjZ$NfyZS;A=Ai_*P0&$~V#gdq%B8CbA6le1!kF1iibze~my
z$&}6zYWZiMF
zYl+G&N=oz0_F3x_ckN^1($HzbEa`Hs_x {|qZL?J=j`R1&(
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 |