forked from microsoft/copilot-for-eclipse
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCopilotUi.java
More file actions
260 lines (224 loc) · 10.2 KB
/
Copy pathCopilotUi.java
File metadata and controls
260 lines (224 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
package com.microsoft.copilot.eclipse.ui;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import org.eclipse.core.net.proxy.IProxyService;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.core.runtime.preferences.ConfigurationScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.jface.preference.IPreferenceStore;
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 org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
import org.osgi.service.prefs.BackingStoreException;
import com.microsoft.copilot.eclipse.core.Constants;
import com.microsoft.copilot.eclipse.core.CopilotCore;
import com.microsoft.copilot.eclipse.core.lsp.CopilotLanguageServerConnection;
import com.microsoft.copilot.eclipse.ui.chat.services.ChatServiceManager;
import com.microsoft.copilot.eclipse.ui.completion.EditorLifecycleListener;
import com.microsoft.copilot.eclipse.ui.completion.EditorsManager;
import com.microsoft.copilot.eclipse.ui.preferences.LanguageServerSettingManager;
import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
import com.microsoft.copilot.eclipse.ui.utils.UiUtils;
/**
* The plug-in runtime class for the Copilot plug-in containing the UI support, like dialogs, ghost text rendering, etc.
*/
public class CopilotUi extends AbstractUIPlugin {
private static CopilotUi COPILOT_UI_PLUGIN = null;
private CopilotStatusManager copilotStatusManager;
private EditorLifecycleListener editorLifecycleListener;
private EditorsManager editorsManager;
private ChatServiceManager chatServiceManager;
private LanguageServerSettingManager settingMgr;
public static final String INIT_JOB_FAMILY = "com.microsoft.copilot.eclipse.ui.initJob";
/**
* Creates the Copilot ui plugin. The plugin is created automatically by the Eclipse framework. Clients must not call
* this constructor.
*/
public CopilotUi() {
super();
COPILOT_UI_PLUGIN = this;
}
public static CopilotUi getPlugin() {
return COPILOT_UI_PLUGIN;
}
@Override
public void start(BundleContext context) throws Exception {
// Explicitly call method from core to ensure core is activated
CopilotCore.getPlugin();
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);
} catch (OperationCanceledException | InterruptedException e) {
CopilotCore.LOGGER.error(e);
return Status.error("Failed to initialize GitHub Copilot plugin.", e);
}
CopilotLanguageServerConnection connection = CopilotCore.getPlugin().getCopilotLanguageServer();
if (connection == null) {
var ex = new IllegalStateException("Failed to start copilot language server.");
CopilotCore.LOGGER.error(ex);
throw ex;
}
// Initialize the setting manager then sync the configuration to CLS. This needs to
// happen in early stage to let CLS know the proxy setting if user configured.
// NOTE: All network related operation needs to happen AFTER syncConfiguration().
ServiceReference<?> serviceReference = context.getServiceReference(IProxyService.class.getName());
settingMgr = new LanguageServerSettingManager(CopilotCore.getPlugin().getCopilotLanguageServer(),
(IProxyService) context.getService(serviceReference), getPreferenceStore());
settingMgr.syncConfiguration();
try {
// call checkStatus synchronously (blocking) to get the user name, which will be used
// to recover the user's specific configurations.
CopilotCore.getPlugin().getAuthStatusManager().checkStatus().get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
CopilotCore.LOGGER.error("Failed to check authentication status", e);
} catch (ExecutionException e) {
CopilotCore.LOGGER.error("Failed to check authentication status", e);
}
CopilotUi.this.editorsManager = new EditorsManager(connection, CopilotCore.getPlugin().getCompletionProvider(),
CopilotCore.getPlugin().getNextEditSuggestionProvider(), settingMgr);
CopilotUi.this.editorLifecycleListener = new EditorLifecycleListener(connection, editorsManager);
CopilotUi.this.chatServiceManager = new ChatServiceManager();
// inject the chat service manager into the core plugin, so that it can be used to handle
// some server to client request that needs to be handled with UI logics.
CopilotCore.getPlugin().setChatServiceManager(chatServiceManager);
CopilotUi.this.copilotStatusManager = new CopilotStatusManager();
settingMgr.syncMcpRegistrationConfiguration();
registerPartListener();
// Initialize the completion handler for the active editor in case we miss the event
// to initialize it.
initCompletionHandlerForActiveEditor();
showHintIfNecessary(context);
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(Object family) {
return INIT_JOB_FAMILY.equals(family);
}
};
initJob.setSystem(true);
initJob.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
// refresh the menu icon in case we miss the event to refresh it.
UiUtils.refreshCopilotMenu();
}
});
initJob.schedule();
}
private void showHintIfNecessary(BundleContext context) {
IPreferenceStore preferenceStore = CopilotUi.getPlugin().getPreferenceStore();
IEclipsePreferences configPrefs = ConfigurationScope.INSTANCE
.getNode(CopilotUi.getPlugin().getBundle().getSymbolicName());
boolean needToFlush = false;
int storedQuickStartVersion = Math.max(getIntPreference(Constants.COPILOT_QUICK_START_VERSION, 0),
preferenceStore.getInt(Constants.QUICK_START_VERSION));
if (storedQuickStartVersion < Constants.CURRENT_COPILOT_QUICK_START_VERSION) {
SwtUtils.invokeOnDisplayThreadAsync(
() -> UiUtils.executeCommandWithParameters(UiConstants.OPEN_QUICK_START_COMMAND_ID, null));
configPrefs.putInt(Constants.COPILOT_QUICK_START_VERSION, Constants.CURRENT_COPILOT_QUICK_START_VERSION);
needToFlush = true;
}
if (getBooleanPreference(Constants.AUTO_SHOW_WHAT_IS_NEW, true)) {
String lastUsedVersion = getStringPreference(Constants.LAST_USED_COPILOT_PLUGIN_VERSION, "");
Version bundleVersion = context.getBundle().getVersion();
String currentVersion = bundleVersion.getMajor() + "." + bundleVersion.getMinor();
if (!Objects.equals(lastUsedVersion, currentVersion)) {
SwtUtils.invokeOnDisplayThreadAsync(
() -> UiUtils.executeCommandWithParameters(UiConstants.OPEN_WHATS_NEW_COMMAND_ID, null));
configPrefs.put(Constants.LAST_USED_COPILOT_PLUGIN_VERSION, currentVersion);
needToFlush = true;
}
}
if (needToFlush) {
try {
configPrefs.flush();
} catch (BackingStoreException e) {
CopilotCore.LOGGER.error("Failed to persist hint in ConfigurationScope", e);
}
}
}
/**
* Reads the boolean preference from all scopes.
*
* @return preference value considering all scopes (config, instance and product)
*/
public static boolean getBooleanPreference(String key, boolean defaultValue) {
String bundleId = CopilotUi.getPlugin().getBundle().getSymbolicName();
return Platform.getPreferencesService().getBoolean(bundleId, key, defaultValue, null);
}
/**
* Reads the int preference from all scopes.
*
* @return preference value considering all scopes (config, instance and product)
*/
public static int getIntPreference(String key, int defaultValue) {
String bundleId = CopilotUi.getPlugin().getBundle().getSymbolicName();
return Platform.getPreferencesService().getInt(bundleId, key, defaultValue, null);
}
/**
* Read the String preference from all scopes.
*
* @return preference value considering all scopes (config, instance and product)
*/
public static String getStringPreference(String key, String defaultValue) {
String bundleId = CopilotUi.getPlugin().getBundle().getSymbolicName();
return Platform.getPreferencesService().getString(bundleId, key, defaultValue, null);
}
public LanguageServerSettingManager getLanguageServerSettingManager() {
return settingMgr;
}
@Override
public void stop(BundleContext context) throws Exception {
unregisterPartListener();
if (this.editorsManager != null) {
this.editorsManager.dispose();
}
if (this.settingMgr != null) {
this.settingMgr.dispose();
}
if (this.chatServiceManager != null) {
this.chatServiceManager.dispose();
}
}
public EditorsManager getEditorsManager() {
return editorsManager;
}
public ChatServiceManager getChatServiceManager() {
return chatServiceManager;
}
private void registerPartListener() {
IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
for (IWorkbenchWindow window : windows) {
window.getPartService().addPartListener(this.editorLifecycleListener);
}
}
private void initCompletionHandlerForActiveEditor() {
IEditorPart editorPart = SwtUtils.getActiveEditorPart();
if (editorPart != null) {
this.editorLifecycleListener.partActivated(editorPart);
}
}
private void unregisterPartListener() {
IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
for (IWorkbenchWindow window : windows) {
window.getPartService().removePartListener(this.editorLifecycleListener);
}
}
}