diff --git a/CodeiumVS/CodeiumVS.csproj b/CodeiumVS/CodeiumVS.csproj
index 06eb4dc..ad61fbe 100644
--- a/CodeiumVS/CodeiumVS.csproj
+++ b/CodeiumVS/CodeiumVS.csproj
@@ -69,6 +69,7 @@
+
diff --git a/CodeiumVS/CodeiumVSPackage.cs b/CodeiumVS/CodeiumVSPackage.cs
index f73bf69..7090a36 100644
--- a/CodeiumVS/CodeiumVSPackage.cs
+++ b/CodeiumVS/CodeiumVSPackage.cs
@@ -192,7 +192,7 @@ static string CleanifyBrowserPath(string p)
}
// Try three different ways to open url in the default browser
- public void OpenInBrowser(string url)
+ public static void OpenInBrowser(string url)
{
Action[] methods = [
(_url) => {
@@ -215,11 +215,11 @@ public void OpenInBrowser(string url)
}
catch (Exception ex)
{
- Log($"Could not open in browser, encountered an exception: {ex}\n Retrying using another method");
+ Instance?.Log($"Could not open in browser, encountered an exception: {ex}\n Retrying using another method");
}
}
- Log($"Codeium failed to open the browser, please use this URL instead: {url}");
+ Instance?.Log($"Codeium failed to open the browser, please use this URL instead: {url}");
VS.MessageBox.Show("Codeium: Failed to open browser", $"Please use this URL instead (you can copy from the output window):\n{url}");
}
diff --git a/CodeiumVS/LanguageServer/LanguageServer.cs b/CodeiumVS/LanguageServer/LanguageServer.cs
index 8fb1b4a..bfaad2e 100644
--- a/CodeiumVS/LanguageServer/LanguageServer.cs
+++ b/CodeiumVS/LanguageServer/LanguageServer.cs
@@ -1,17 +1,19 @@
using CodeiumVS.Packets;
-using EnvDTE80;
using Microsoft.VisualStudio;
+using Microsoft.VisualStudio.Imaging;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Threading;
using Newtonsoft.Json;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
-using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
@@ -21,476 +23,688 @@ namespace CodeiumVS;
public class LanguageServer
{
- private const string Version = "1.6.10";
-
- private int Port = 0;
- private Process process;
-
- private readonly Metadata Metadata;
- private readonly HttpClient HttpClient;
- private readonly CodeiumVSPackage Package;
- private readonly NotificationInfoBar NotificationDownloading;
-
- public readonly LanguageServerController Controller;
-
- public LanguageServer()
- {
- NotificationDownloading = new NotificationInfoBar();
-
- Package = CodeiumVSPackage.Instance;
- HttpClient = new HttpClient();
- Controller = new LanguageServerController();
- Metadata = new();
- }
-
- public async Task InitializeAsync()
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- string ideVersion = "17.0", locale = "en-US";
-
+ private string _languageServerURL;
+ private string _languageServerVersion = "1.6.10";
+
+ private int _port = 0;
+ private Process _process;
+
+ private readonly Metadata _metadata;
+ private readonly HttpClient _httpClient;
+ private readonly CodeiumVSPackage _package;
+
+ public readonly LanguageServerController Controller;
+
+ public LanguageServer()
+ {
+ _package = CodeiumVSPackage.Instance;
+ _metadata = new();
+ _httpClient = new HttpClient();
+ Controller = new LanguageServerController();
+ }
+
+ public async Task InitializeAsync()
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+
+ string ideVersion = "17.0", locale = "en-US";
+
+ try
+ {
+ locale = CultureInfo.CurrentUICulture.Name;
+ Version? version = await VS.Shell.GetVsVersionAsync();
+ if (version != null) ideVersion = version.ToString();
+ }
+ catch (Exception) { }
+
+ // must be called before setting the metadata to retrieve _languageServerVersion first
+ await PrepareAsync();
+
+ _metadata.request_id = 0;
+ _metadata.ide_name = "visual_studio";
+ _metadata.ide_version = ideVersion;
+ _metadata.extension_name = Vsix.Name;
+ _metadata.extension_version = _languageServerVersion;
+ _metadata.session_id = Guid.NewGuid().ToString();
+ _metadata.locale = locale;
+ _metadata.disable_telemetry = false;
+ }
+
+ public void Dispose()
+ {
+ // HasExited can throw, i don't know we should properly handle it
try
{
- locale = CultureInfo.CurrentUICulture.Name;
- Version? version = await VS.Shell.GetVsVersionAsync();
- if (version != null) ideVersion = version.ToString();
- }
- catch (Exception) { }
-
- Metadata.request_id = 0;
- Metadata.ide_name = "visual_studio";
- Metadata.ide_version = ideVersion;
- Metadata.extension_name = Vsix.Name;
- Metadata.extension_version = Version;
- Metadata.session_id = Guid.NewGuid().ToString();
- Metadata.locale = locale;
- Metadata.disable_telemetry = false;
-
- await PrepareAsync();
- }
-
- public void Dispose()
- {
- if (process != null && !process.HasExited)
- {
- process.Kill();
- process.Dispose();
- process = null;
- }
-
- Controller.Disconnect();
- }
-
- public int GetPort() { return Port; }
- public string GetKey() { return Metadata.api_key; }
- public string GetVersion() { return Version; }
- public bool IsReady() { return Port != 0; }
- public async Task WaitReadyAsync() { while (!IsReady()) {await Task.Delay(50);} }
-
- // Get API key from the authentication token
- public async Task SignInWithAuthTokenAsync(string authToken)
- {
- string url = Package.SettingsPage.EnterpriseMode ?
- Package.SettingsPage.ApiUrl + "/exa.seat_management_pb.SeatManagementService/RegisterUser" :
- "https://api.codeium.com/register_user/";
-
- RegisterUserRequest data = new() { firebase_id_token = authToken };
- RegisterUserResponse result = await RequestUrlAsync(url, data);
-
- Metadata.api_key = result.api_key;
-
- if (Metadata.api_key == null)
- {
- await Package.LogAsync("Failed to sign in.");
-
- // show an error message box
- var msgboxResult = await VS.MessageBox.ShowAsync(
- "Codeium: Failed to sign in. Please check the output window for more details.",
- "Do you want to retry?",
- OLEMSGICON.OLEMSGICON_INFO,
- OLEMSGBUTTON.OLEMSGBUTTON_RETRYCANCEL,
- OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST
- );
-
- if (msgboxResult == VSConstants.MessageBoxResult.IDRETRY)
- await SignInWithAuthTokenAsync(authToken);
-
- return;
- }
-
- File.WriteAllText(Package.GetAPIKeyPath(), Metadata.api_key);
- await Package.LogAsync("Signed in successfully");
- await Package.UpdateSignedInStateAsync();
- }
-
- // Open the browser to sign in
- public async Task SignInAsync()
- {
- // this will block until the sign in process has finished
- async Task WaitForAuthTokenAsync()
- {
- // wait until we got the actual port of the LSP
- await WaitReadyAsync();
-
- // TODO: should we use timeout = Timeout.InfiniteTimeSpan? default value is 100s (1m40s)
- GetAuthTokenResponse? result = await RequestCommandAsync("GetAuthToken", new {});
-
- if (result == null)
+ if (_process != null && !_process.HasExited)
{
- // show an error message box
- var msgboxResult = await VS.MessageBox.ShowAsync(
- "Codeium: Failed to get the Authentication Token. Please check the output window for more details.",
- "Do you want to retry?",
- OLEMSGICON.OLEMSGICON_INFO,
- OLEMSGBUTTON.OLEMSGBUTTON_RETRYCANCEL,
- OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST
- );
-
- return (msgboxResult == VSConstants.MessageBoxResult.IDRETRY) ? await WaitForAuthTokenAsync() : null;
+ _process.Kill();
+ _process.Dispose();
+ _process = null;
}
-
- return result.authToken;
}
-
- string state = Guid.NewGuid().ToString();
- string portalUrl = Package.SettingsPage.EnterpriseMode ? Package.SettingsPage.PortalUrl : "https://www.codeium.com";
- string redirectUrl = Uri.EscapeDataString($"http://127.0.0.1:{Port}/auth");
- string url = $"{portalUrl}/profile?response_type=token&redirect_uri={redirectUrl}&state={state}&scope=openid%20profile%20email&redirect_parameters_type=query";
-
- await Package.LogAsync("Opening browser to " + url);
-
- Package.OpenInBrowser(url);
-
- string authToken = await WaitForAuthTokenAsync();
- if (authToken != null) await SignInWithAuthTokenAsync(authToken);
- }
-
- // Delete the stored API key
- public async Task SignOutAsync()
- {
- Metadata.api_key = "";
- File.Delete(Package.GetAPIKeyPath());
- await Package.LogAsync("Signed out successfully");
- await Package.UpdateSignedInStateAsync();
- }
-
- // Download the language server (if not already) and start it
- public async Task PrepareAsync()
- {
- string binaryPath = Package.GetLanguageServerPath();
-
- if (File.Exists(binaryPath))
+ catch (Exception) { }
+
+ Controller.Disconnect();
+ }
+
+ public int GetPort() { return _port; }
+ public string GetKey() { return _metadata.api_key; }
+ public string GetVersion() { return _languageServerVersion; }
+ public bool IsReady() { return _port != 0; }
+ public async Task WaitReadyAsync() { while (!IsReady()) {await Task.Delay(50);} }
+
+ // Get API key from the authentication token
+ public async Task SignInWithAuthTokenAsync(string authToken)
+ {
+ string url = _package.SettingsPage.EnterpriseMode ?
+ _package.SettingsPage.ApiUrl + "/exa.seat_management_pb.SeatManagementService/RegisterUser" :
+ "https://api.codeium.com/register_user/";
+
+ RegisterUserRequest data = new() { firebase_id_token = authToken };
+ RegisterUserResponse result = await RequestUrlAsync(url, data);
+
+ _metadata.api_key = result.api_key;
+
+ if (_metadata.api_key == null)
+ {
+ await _package.LogAsync("Failed to sign in.");
+
+ // show an error message box
+ var msgboxResult = await VS.MessageBox.ShowAsync(
+ "Codeium: Failed to sign in. Please check the output window for more details.",
+ "Do you want to retry?",
+ OLEMSGICON.OLEMSGICON_WARNING,
+ OLEMSGBUTTON.OLEMSGBUTTON_RETRYCANCEL,
+ OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST
+ );
+
+ if (msgboxResult == VSConstants.MessageBoxResult.IDRETRY)
+ await SignInWithAuthTokenAsync(authToken);
+
+ return;
+ }
+
+ File.WriteAllText(_package.GetAPIKeyPath(), _metadata.api_key);
+ await _package.LogAsync("Signed in successfully");
+ await _package.UpdateSignedInStateAsync();
+ }
+
+ // Open the browser to sign in
+ public async Task SignInAsync()
+ {
+ // this will block until the sign in process has finished
+ async Task WaitForAuthTokenAsync()
+ {
+ // wait until we got the actual port of the LSP
+ await WaitReadyAsync();
+
+ // TODO: should we use timeout = Timeout.InfiniteTimeSpan? default value is 100s (1m40s)
+ GetAuthTokenResponse? result = await RequestCommandAsync("GetAuthToken", new {});
+
+ if (result == null)
+ {
+ // show an error message box
+ var msgboxResult = await VS.MessageBox.ShowAsync(
+ "Codeium: Failed to get the Authentication Token. Please check the output window for more details.",
+ "Do you want to retry?",
+ OLEMSGICON.OLEMSGICON_WARNING,
+ OLEMSGBUTTON.OLEMSGBUTTON_RETRYCANCEL,
+ OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST
+ );
+
+ return (msgboxResult == VSConstants.MessageBoxResult.IDRETRY) ? await WaitForAuthTokenAsync() : null;
+ }
+
+ return result.authToken;
+ }
+
+ string state = Guid.NewGuid().ToString();
+ string portalUrl = _package.SettingsPage.EnterpriseMode ? _package.SettingsPage.PortalUrl : "https://www.codeium.com";
+ string redirectUrl = Uri.EscapeDataString($"http://127.0.0.1:{_port}/auth");
+ string url = $"{portalUrl}/profile?response_type=token&redirect_uri={redirectUrl}&state={state}&scope=openid%20profile%20email&redirect_parameters_type=query";
+
+ await _package.LogAsync("Opening browser to " + url);
+
+ CodeiumVSPackage.OpenInBrowser(url);
+
+ string authToken = await WaitForAuthTokenAsync();
+ if (authToken != null) await SignInWithAuthTokenAsync(authToken);
+ }
+
+ // Delete the stored API key
+ public async Task SignOutAsync()
+ {
+ _metadata.api_key = "";
+ Utilities.FileUtilities.DeleteSafe(_package.GetAPIKeyPath());
+ await _package.LogAsync("Signed out successfully");
+ await _package.UpdateSignedInStateAsync();
+ }
+
+ ///
+ /// Get the language server URL and version, from the portal if we are in enterprise mode
+ ///
+ ///
+ private async Task GetLanguageServerInfoAsync()
+ {
+ string extensionBaseUrl = "https://github.com/Exafunction/codeium/releases/download";
+
+ if (_package.SettingsPage.EnterpriseMode)
+ {
+ // Get the contents of /api/extension_base_url
+ try
+ {
+ string portalUrl = _package.SettingsPage.PortalUrl.TrimEnd('/');
+ string result = await new HttpClient().GetStringAsync(portalUrl + "/api/extension_base_url");
+ extensionBaseUrl = result.Trim().TrimEnd('/');
+ _languageServerVersion = await new HttpClient().GetStringAsync(portalUrl + "/api/version");
+ }
+ catch (Exception)
+ {
+ await _package.LogAsync("Failed to get extension base url");
+ extensionBaseUrl = "https://github.com/Exafunction/codeium/releases/download";
+ }
+ }
+
+ _languageServerURL = $"{extensionBaseUrl}/language-server-v{_languageServerVersion}/language_server_windows_x64.exe.gz";
+ }
+
+ ///
+ /// Update the progress dialog percentage
+ ///
+ private async Task ThreadDownload_UpdateProgressAsync(DownloadProgressChangedEventArgs e, IVsThreadedWaitDialog4 progressDialog)
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+
+ double totalBytesMb = e.TotalBytesToReceive / 1024.0 / 1024.0;
+ double recievedBytesMb = e.BytesReceived / 1024.0 / 1024.0;
+
+ progressDialog.UpdateProgress(
+ $"Downloading language server v{_languageServerVersion} ({e.ProgressPercentage}%)",
+ $"{recievedBytesMb:f2}Mb / {totalBytesMb:f2}Mb",
+ $"Codeium: Downloading language server v{_languageServerVersion} ({e.ProgressPercentage}%)",
+ (int)e.BytesReceived, (int)e.TotalBytesToReceive, true, out _
+ );
+ }
+
+ ///
+ /// On download completed, extract the language server from the archive and start it. Prompt the user to retry if failed.
+ ///
+ private async Task ThreadDownload_OnCompletedAsync(AsyncCompletedEventArgs e, IVsThreadedWaitDialog4 progressDialog, string downloadDest)
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+
+ progressDialog.StartWaitDialog(
+ "Codeium", $"Extracting files...", "Almost done", null,
+ $"Codeium: Extracting files...", 0, false, true
+ );
+
+ // show a notification to ask the user if they wish to retry downloading it
+ if (e.Error != null)
{
- await StartAsync();
- return;
- }
-
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
- await Package.LogAsync("Downloading language server...");
-
- // show the downloading progress dialog
- var waitDialogFactory = (IVsThreadedWaitDialogFactory)await VS.Services.GetThreadedWaitDialogAsync();
- IVsThreadedWaitDialog4 progDialog = waitDialogFactory.CreateInstance();
+ await _package.LogAsync($"ThreadDownload_OnCompletedAsync: Failed to download the language server; Exception: {e.Error}");
+ NotificationInfoBar errorBar = new();
+ KeyValuePair[] actions = [
+ new KeyValuePair("Retry", delegate
+ {
+ ThreadHelper.JoinableTaskFactory.RunAsync(async delegate
+ {
+ await errorBar.CloseAsync();
+ await PrepareAsync();
+ }).FireAndForget();
+ }),
+ ];
+
+ errorBar.Show(
+ "[Codeium] Critical Error: Failed to download the language server. Do you want to retry?",
+ KnownMonikers.StatusError, true, null, [.. actions, .. NotificationInfoBar.SupportActions]
+ );
+ }
+ else
+ {
+ // extract the language server archive
+ await _package.LogAsync("Extracting language server...");
+ using FileStream fileStream = new(downloadDest, FileMode.Open);
+ using GZipStream gzipStream = new(fileStream, CompressionMode.Decompress);
+ using FileStream outputStream = new(_package.GetLanguageServerPath(), FileMode.Create);
- string extensionBaseUrl = "https://github.com/Exafunction/codeium/releases/download";
- string languageServerVersion = Version;
- if (Package.SettingsPage.EnterpriseMode)
- {
- // Get the contents of /api/extension_base_url
+ // if there were an error during extraction, the `StartAsync`
+ // function can handle it, so we don't need to do it here
try
{
- string portalUrl = Package.SettingsPage.PortalUrl.TrimEnd('/');
- string result = await new HttpClient().GetStringAsync(portalUrl + "/api/extension_base_url");
- extensionBaseUrl = result.Trim().TrimEnd('/');
- languageServerVersion = await new HttpClient().GetStringAsync(portalUrl + "/api/version");
+ await gzipStream.CopyToAsync(outputStream);
}
- catch (Exception)
+ catch (Exception ex)
{
- await Package.LogAsync("Failed to get extension base url");
- extensionBaseUrl = "https://github.com/Exafunction/codeium/releases/download";
+ await _package.LogAsync($"ThreadDownload_OnCompletedAsync: Error during extraction; Exception: {ex}");
}
- }
- Metadata.extension_version = languageServerVersion;
-
- progDialog.StartWaitDialog(
- "Codeium", $"Downloading language server v{languageServerVersion}", "", null,
- $"Codeium: Downloading language server v{languageServerVersion}", 0, false, true
- );
- // the language server is downloaded in a thread so that it doesn't block the UI
- // if we remove `while (webClient.IsBusy)`, the DownloadProgressChanged callback won't be called
- // until VS is closing, not sure how we can fix that without spawning a separate thread
- void ThreadDownloadLanguageServer()
- {
- string langServerFolder = Package.GetLanguageServerFolder();
- string downloadDest = Path.Combine(langServerFolder, "language-server.gz");
+ outputStream.Close();
+ gzipStream.Close();
+ fileStream.Close();
+ }
- Directory.CreateDirectory(langServerFolder);
- if (File.Exists(downloadDest)) File.Delete(downloadDest);
+ Utilities.FileUtilities.DeleteSafe(downloadDest);
+
+ progressDialog.EndWaitDialog();
+ (progressDialog as IDisposable)?.Dispose();
+
+ if (e.Error == null) await StartAsync();
+ }
+
+ ///
+ /// The language server is downloaded in a thread so that it doesn't block the UI.
+ /// Iff we remove `while (webClient.IsBusy)`, the DownloadProgressChanged callback won't be called
+ /// until VS is closing, not sure how we can fix that without spawning a separate thread.
+ ///
+ ///
+ private void ThreadDownloadLanguageServer(IVsThreadedWaitDialog4 progressDialog)
+ {
+ string langServerFolder = _package.GetLanguageServerFolder();
+ string downloadDest = Path.GetTempFileName();
+
+ Directory.CreateDirectory(langServerFolder);
+ Utilities.FileUtilities.DeleteSafe(downloadDest);
+
+ Uri url = new(_languageServerURL);
+ WebClient webClient = new();
+
+ int oldPercent = -1;
+
+ webClient.DownloadProgressChanged += (s, e) =>
+ {
+ // don't update the progress bar too often
+ if (e.ProgressPercentage == oldPercent) return;
+ oldPercent = e.ProgressPercentage;
+
+ ThreadHelper.JoinableTaskFactory.RunAsync(async delegate
+ {
+ await ThreadDownload_UpdateProgressAsync(e, progressDialog);
+ }).FireAndForget();
+ };
+
+ webClient.DownloadFileCompleted += (s, e) =>
+ {
+ ThreadHelper.JoinableTaskFactory.RunAsync(async delegate
+ {
+ await ThreadDownload_OnCompletedAsync(e, progressDialog, downloadDest);
+ }).FireAndForget();
+ };
+
+ // set no-cache so that we don't have unexpected problems
+ webClient.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
+
+ // start downloading and wait for it to finish
+ webClient.DownloadFileAsync(url, downloadDest);
+
+ // wait until the download is completed
+ while (webClient.IsBusy)
+ Thread.Sleep(100);
+
+ webClient.Dispose();
+ }
+
+ // Download the language server (if not already) and start it
+ public async Task PrepareAsync()
+ {
+ await GetLanguageServerInfoAsync();
+ string binaryPath = _package.GetLanguageServerPath();
+
+ if (File.Exists(binaryPath))
+ {
+ await StartAsync();
+ return;
+ }
+
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ await _package.LogAsync($"Downloading language server v{_languageServerVersion}");
+
+ // show the downloading progress dialog before starting the thread to make it feels more responsive
+ var waitDialogFactory = (IVsThreadedWaitDialogFactory)await VS.Services.GetThreadedWaitDialogAsync();
+ IVsThreadedWaitDialog4 progressDialog = waitDialogFactory.CreateInstance();
+
+ progressDialog.StartWaitDialog(
+ "Codeium", $"Downloading language server v{_languageServerVersion}", "", null,
+ $"Codeium: Downloading language server v{_languageServerVersion}", 0, false, true
+ );
+
+ Thread trd = new(() => ThreadDownloadLanguageServer(progressDialog))
+ {
+ IsBackground = true
+ };
+
+ trd.Start();
+ }
+
+ ///
+ /// Verify the language server digital signature. If invalid, prompt the user to re-download it, or ignore and continue.
+ ///
+ /// False if the signature is invalid
+ private async Task VerifyLanguageServerSignatureAsync()
+ {
+ try
+ {
+ X509Certificate2 certificate = new(_package.GetLanguageServerPath());
+ RSACryptoServiceProvider publicKey = (RSACryptoServiceProvider)certificate.PublicKey.Key;
+ if (certificate.Verify()) return true;
+ }
+ catch (CryptographicException) { }
- Uri url = new($"{extensionBaseUrl}/language-server-v{languageServerVersion}/language_server_windows_x64.exe.gz");
- WebClient webClient = new();
+ await _package.LogAsync("LanguageServer.VerifyLanguageServerSignatureAsync: Failed to verify the language server digital signature");
- int oldPercent = -1;
- webClient.DownloadProgressChanged += (s, e) =>
+ NotificationInfoBar errorBar = new();
+ KeyValuePair[] actions = [
+ new KeyValuePair("Re-download", delegate
{
- // don't update the progress bar too often
- if (e.ProgressPercentage != oldPercent)
+ // delete the language server exe and try to re-download the language server
+ ThreadHelper.JoinableTaskFactory.RunAsync(async delegate
{
- oldPercent = e.ProgressPercentage;
- ThreadHelper.JoinableTaskFactory.RunAsync(async delegate
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- double totalBytesMb = e.TotalBytesToReceive / 1024.0 / 1024.0;
- double recievedBytesMb = e.BytesReceived / 1024.0 / 1024.0;
-
- progDialog.UpdateProgress(
- $"Downloading language server v{languageServerVersion} ({e.ProgressPercentage}%)",
- $"{recievedBytesMb:f2}Mb / {totalBytesMb:f2}Mb",
- $"Codeium: Downloading language server v{languageServerVersion} ({e.ProgressPercentage}%)",
- (int)e.BytesReceived, (int)e.TotalBytesToReceive, true, out _
- );
-
- }).FireAndForget(true);
- }
- };
-
- webClient.DownloadFileCompleted += (s, e) =>
+ Utilities.FileUtilities.DeleteSafe(_package.GetLanguageServerPath());
+ await errorBar.CloseAsync();
+ await PrepareAsync();
+ }).FireAndForget();
+ }),
+ new KeyValuePair("Ignore and continue", delegate
{
+ // ignore the invalid signature and just try to start the language server
ThreadHelper.JoinableTaskFactory.RunAsync(async delegate
{
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- progDialog.StartWaitDialog(
- "Codeium", $"Extracting files...", "Almost done", null,
- $"Codeium: Extracting files...", 0, false, true
- );
-
- await Package.LogAsync("Extracting language server...");
- using FileStream fileStream = new(downloadDest, FileMode.Open);
- using GZipStream gzipStream = new(fileStream, CompressionMode.Decompress);
- using FileStream outputStream = new(Package.GetLanguageServerPath(), FileMode.Create);
- await gzipStream.CopyToAsync(outputStream);
-
- outputStream.Close();
- gzipStream.Close();
- fileStream.Close();
-
- progDialog.EndWaitDialog();
- (progDialog as IDisposable).Dispose();
-
- await StartAsync();
- }).FireAndForget(true);
- };
-
- // set no-cache so that we don't have unexpected problems
- webClient.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
-
- // start downloading and wait for it to finish
- webClient.DownloadFileAsync(url, downloadDest);
+ await errorBar.CloseAsync();
+ await StartAsync(true);
+ }).FireAndForget();
+ }),
+ ];
- while (webClient.IsBusy)
- Thread.Sleep(100);
- }
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ errorBar.Show(
+ "[Codeium] Failed to verify the language server digital signature. The executable might be corrupted.",
+ KnownMonikers.IntellisenseWarning, true, null, actions
+ );
- Thread trd = new(new ThreadStart(ThreadDownloadLanguageServer))
- {
- IsBackground = true
- };
- trd.Start();
+ return false;
}
- // Start the language server process
- private async Task StartAsync()
- {
- Port = 0;
+ ///
+ /// Start the language server process and begin reading its pipe output.
+ ///
+ /// If true, ignore the digital signature verification
+ private async Task StartAsync(bool ignoreDigitalSignature = false)
+ {
+ _port = 0;
- string apiUrl = (Package.SettingsPage.ApiUrl.Equals("") ? "https://server.codeium.com" : Package.SettingsPage.ApiUrl);
- string managerDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
- string databaseDir = Package.GetDatabaseDirectory();
+ if (!ignoreDigitalSignature && !await VerifyLanguageServerSignatureAsync())
+ return;
+
+ string apiUrl = (_package.SettingsPage.ApiUrl.Equals("") ? "https://server.codeium.com" : _package.SettingsPage.ApiUrl);
+ string managerDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ string databaseDir = _package.GetDatabaseDirectory();
+ string languageServerPath = _package.GetLanguageServerPath();
try
- {
- Directory.CreateDirectory(managerDir);
- Directory.CreateDirectory(databaseDir);
- }
- catch (Exception ex)
- {
- await Package.LogAsync($"LanguageServer.StartAsync: Failed to create directories; Exception: {ex}");
- await VS.MessageBox.ShowErrorAsync(
- "Codeium: Failed to create language server directories.",
- "Please check the output window for more details."
+ {
+ Directory.CreateDirectory(managerDir);
+ Directory.CreateDirectory(databaseDir);
+ }
+ catch (Exception ex)
+ {
+ await _package.LogAsync($"LanguageServer.StartAsync: Failed to create directories; Exception: {ex}");
+
+ new NotificationInfoBar().Show(
+ "[Codeium] Critical error: Failed to create language server directories. Please check the output window for more details.",
+ KnownMonikers.StatusError, true, null, NotificationInfoBar.SupportActions
);
return;
- }
+ }
- process = new();
- process.StartInfo.FileName = Package.GetLanguageServerPath();
- process.StartInfo.UseShellExecute = false;
- process.StartInfo.CreateNoWindow = true;
- process.StartInfo.RedirectStandardError = true;
- process.EnableRaisingEvents = true;
+ _process = new();
+ _process.StartInfo.FileName = languageServerPath;
+ _process.StartInfo.UseShellExecute = false;
+ _process.StartInfo.CreateNoWindow = true;
+ _process.StartInfo.RedirectStandardError = true;
+ _process.EnableRaisingEvents = true;
- process.StartInfo.Arguments =
- $"--api_server_url {apiUrl} --manager_dir \"{managerDir}\" --database_dir \"{databaseDir}\"" +
- " --enable_chat_web_server --enable_chat_client --detect_proxy=false";
+ _process.StartInfo.Arguments =
+ $"--api_server_url {apiUrl} --manager_dir \"{managerDir}\" --database_dir \"{databaseDir}\"" +
+ " --enable_chat_web_server --enable_chat_client --detect_proxy=false";
- if (Package.SettingsPage.EnterpriseMode)
- process.StartInfo.Arguments += $" --enterprise_mode --portal_url {Package.SettingsPage.PortalUrl}";
+ if (_package.SettingsPage.EnterpriseMode)
+ _process.StartInfo.Arguments += $" --enterprise_mode --portal_url {_package.SettingsPage.PortalUrl}";
- process.ErrorDataReceived += LSP_OnPipeDataReceived;
- process.Exited += LSP_OnExited;
+ _process.ErrorDataReceived += LSP_OnPipeDataReceived;
+ _process.Exited += LSP_OnExited;
- await Package.LogAsync("Starting language server");
+ await _package.LogAsync("Starting language server");
+ // try to start the process, if it fails, prompt the user if they
+ // wish to delete the language server exe and restart VS
try
{
- process.Start();
- process.BeginErrorReadLine();
- Utilities.ProcessExtensions.MakeProcessExitOnParentExit(process);
- }
- catch (Exception ex)
+ _process.Start();
+ }
+ catch (Exception ex)
{
- await Package.LogAsync($"LanguageServer.StartAsync: Failed to start the language server; Exception: {ex}");
- await VS.MessageBox.ShowErrorAsync(
- "Codeium: Failed to start the language server.",
- "Please check the output window for more details."
+ // ask the user if they wish to delete the language server exe and try to re-download it
+
+ _process = null;
+ await _package.LogAsync($"LanguageServer.StartAsync: Failed to start the language server; Exception: {ex}");
+
+ NotificationInfoBar errorBar = new();
+ KeyValuePair[] actions = [
+ new KeyValuePair("Retry", delegate
+ {
+ // delete the language server exe and try to re-download the language server
+ _process = null;
+ Utilities.FileUtilities.DeleteSafe(languageServerPath);
+
+ ThreadHelper.JoinableTaskFactory.RunAsync(async delegate
+ {
+ await errorBar.CloseAsync();
+ await PrepareAsync();
+ }).FireAndForget();
+ }),
+ ];
+
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ errorBar.Show(
+ "[Codeium] Critical Error: Failed to start the language server. Do you want to retry?",
+ KnownMonikers.StatusError, true, null, [.. actions, .. NotificationInfoBar.SupportActions]
);
- }
-
- string apiKeyFilePath = Package.GetAPIKeyPath();
- if (File.Exists(apiKeyFilePath))
- {
- Metadata.api_key = File.ReadAllText(apiKeyFilePath);
- }
-
- await Package.UpdateSignedInStateAsync();
- }
-
- private void LSP_OnExited(object sender, EventArgs e)
- {
- Package.Log("Language Server Process exited unexpectedly, restarting...");
-
- Port = 0;
- process = null;
- Controller.Disconnect();
- ThreadHelper.JoinableTaskFactory.RunAsync(StartAsync).FireAndForget(true);
- }
-
- // This method will be responsible for reading and parsing the output of the LSP
- private void LSP_OnPipeDataReceived(object sender, DataReceivedEventArgs e)
- {
- if (string.IsNullOrEmpty(e.Data)) return;
-
- // regex to match the port number
- Match match = Regex.Match(e.Data, @"Language server listening on (random|fixed) port at (\d{2,5})");
-
- if (match.Success)
- {
- if (int.TryParse(match.Groups[2].Value, out Port))
- {
- Package.Log($"Language server started on port {Port}");
- ChatToolWindow.Instance?.Reload();
- ThreadHelper.JoinableTaskFactory.RunAsync(Controller.ConnectAsync).FireAndForget(true);
- }
- else
- {
- Package.Log($"Error: Failed to parse the port number from \"{match.Groups[1].Value}\"");
- }
+ return;
}
- Package.Log("Language Server: " + e.Data);
- }
-
- private async Task RequestUrlAsync(string url, object data, CancellationToken cancellationToken = default)
- {
- StringContent post_data = new(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
+ // try to read the pipe output, this is a mild error if it fails
try
{
- HttpResponseMessage rq = await HttpClient.PostAsync(url, post_data, cancellationToken);
- if (rq.StatusCode == HttpStatusCode.OK)
- {
- return JsonConvert.DeserializeObject(await rq.Content.ReadAsStringAsync());
- }
-
- await Package.LogAsync($"Error: Failed to send request to {url}, status code: {rq.StatusCode}");
- }
- catch (OperationCanceledException) { }
- catch (Exception ex)
+ _process.BeginErrorReadLine();
+ }
+ catch (Exception ex)
{
- await Package.LogAsync($"Error: Failed to send request to {url}, exception: {ex.Message}");
- }
+ await _package.LogAsync($"LanguageServer.StartAsync: BeginErrorReadLine failed; Exception: {ex}");
- return default;
- }
+ // warn the user about the issue
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ new NotificationInfoBar().Show(
+ "[Codeium] Failed to read output from the language server, Codeium might not work properly.",
+ KnownMonikers.IntellisenseWarning, true, null, NotificationInfoBar.SupportActions
+ );
- private async Task RequestCommandAsync(string command, object data, CancellationToken cancellationToken = default)
- {
- string url = $"http://127.0.0.1:{Port}/exa.language_server_pb.LanguageServerService/{command}";
- return await RequestUrlAsync(url, data, cancellationToken);
- }
+ // fall back to reading the port file
+ var timeoutSec = 120;
+ var elapsedSec = 0;
- public async Task?> GetCompletionsAsync(string absolutePath, string text, Languages.LangInfo language, int cursorPosition, string lineEnding, int tabSize, bool insertSpaces, CancellationToken token)
- {
- GetCompletionsRequest data = new()
- {
- metadata = GetMetadata(),
- document = new()
- {
- text = text,
- editor_language = language.Name,
- language = language.Type,
- cursor_offset = (ulong)cursorPosition,
- line_ending = lineEnding,
- absolute_path = absolutePath,
- relative_path = Path.GetFileName(absolutePath)
- },
- editor_options = new()
+ while (elapsedSec++ < timeoutSec)
{
- tab_size = (ulong)tabSize,
- insert_spaces = insertSpaces,
- disable_autocomplete_in_comments = !Package.SettingsPage.EnableCommentCompletion,
- }
- };
+ // Check for new files in the directory
+ var files = Directory.GetFiles(managerDir);
- GetCompletionsResponse? result = await RequestCommandAsync("GetCompletions", data, token);
- return result != null ? result.completionItems : [];
- }
+ foreach (var file in files)
+ {
+ if (int.TryParse(Path.GetFileName(file), out _port) && _port != 0)
+ break;
+ }
- public async Task AcceptCompletionAsync(string completionId)
- {
- AcceptCompletionRequest data = new()
- {
- metadata = GetMetadata(),
- completion_id = completionId
- };
+ if (_port != 0) break;
- await RequestCommandAsync("AcceptCompletion", data);
- }
+ // Wait for a short time before checking again
+ await Task.Delay(1000);
+ }
- public async Task GetProcessesAsync()
- {
- return await RequestCommandAsync("GetProcesses", new { });
- }
+ if (_port != 0)
+ {
+ ThreadHelper.JoinableTaskFactory.RunAsync(Controller.ConnectAsync).FireAndForget(true);
+ }
+ else
+ {
+ new NotificationInfoBar().Show(
+ "[Codeium] Critical Error: Failed to get the language server port. Please check the output window for more details.",
+ KnownMonikers.StatusError, true, null, NotificationInfoBar.SupportActions
+ );
- public Metadata GetMetadata()
- {
- return new()
- {
- request_id = Metadata.request_id++,
- api_key = Metadata.api_key,
- ide_name = Metadata.ide_name,
- ide_version = Metadata.ide_version,
- extension_name = Metadata.extension_name,
- extension_version = Metadata.extension_version,
- session_id = Metadata.session_id,
- locale = Metadata.locale,
- disable_telemetry = Metadata.disable_telemetry
- };
- }
+ return;
+ }
+ }
+
+ if (!Utilities.ProcessExtensions.MakeProcessExitOnParentExit(_process))
+ {
+ await _package.LogAsync("LanguageServer.StartAsync: MakeProcessExitOnParentExit failed");
+ }
+
+ string apiKeyFilePath = _package.GetAPIKeyPath();
+ if (File.Exists(apiKeyFilePath))
+ {
+ _metadata.api_key = File.ReadAllText(apiKeyFilePath);
+ }
+
+ await _package.UpdateSignedInStateAsync();
+ }
+
+ private void LSP_OnExited(object sender, EventArgs e)
+ {
+ _package.Log("Language Server Process exited unexpectedly, restarting...");
+
+ _port = 0;
+ _process = null;
+ Controller.Disconnect();
+ ThreadHelper.JoinableTaskFactory.RunAsync(async delegate
+ {
+ await StartAsync();
+
+ }).FireAndForget(true);
+ }
+
+ // This method will be responsible for reading and parsing the output of the LSP
+ private void LSP_OnPipeDataReceived(object sender, DataReceivedEventArgs e)
+ {
+ if (string.IsNullOrEmpty(e.Data)) return;
+
+ // regex to match the port number
+ Match match = Regex.Match(e.Data, @"Language server listening on (random|fixed) port at (\d{2,5})");
+
+ if (match.Success)
+ {
+ if (int.TryParse(match.Groups[2].Value, out _port))
+ {
+ _package.Log($"Language server started on port {_port}");
+
+ ChatToolWindow.Instance?.Reload();
+ ThreadHelper.JoinableTaskFactory.RunAsync(Controller.ConnectAsync).FireAndForget(true);
+ }
+ else
+ {
+ _package.Log($"Error: Failed to parse the port number from \"{match.Groups[1].Value}\"");
+ }
+ }
+
+ _package.Log("Language Server: " + e.Data);
+ }
+
+ private async Task RequestUrlAsync(string url, object data, CancellationToken cancellationToken = default)
+ {
+ StringContent post_data = new(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
+ try
+ {
+ HttpResponseMessage rq = await _httpClient.PostAsync(url, post_data, cancellationToken);
+ if (rq.StatusCode == HttpStatusCode.OK)
+ {
+ return JsonConvert.DeserializeObject(await rq.Content.ReadAsStringAsync());
+ }
+
+ await _package.LogAsync($"Error: Failed to send request to {url}, status code: {rq.StatusCode}");
+ }
+ catch (OperationCanceledException) { }
+ catch (Exception ex)
+ {
+ await _package.LogAsync($"Error: Failed to send request to {url}, exception: {ex.Message}");
+ }
+
+ return default;
+ }
+
+ private async Task RequestCommandAsync(string command, object data, CancellationToken cancellationToken = default)
+ {
+ string url = $"http://127.0.0.1:{_port}/exa.language_server_pb.LanguageServerService/{command}";
+ return await RequestUrlAsync(url, data, cancellationToken);
+ }
+
+ public async Task?> GetCompletionsAsync(string absolutePath, string text, Languages.LangInfo language, int cursorPosition, string lineEnding, int tabSize, bool insertSpaces, CancellationToken token)
+ {
+ GetCompletionsRequest data = new()
+ {
+ metadata = GetMetadata(),
+ document = new()
+ {
+ text = text,
+ editor_language = language.Name,
+ language = language.Type,
+ cursor_offset = (ulong)cursorPosition,
+ line_ending = lineEnding,
+ absolute_path = absolutePath,
+ relative_path = Path.GetFileName(absolutePath)
+ },
+ editor_options = new()
+ {
+ tab_size = (ulong)tabSize,
+ insert_spaces = insertSpaces,
+ disable_autocomplete_in_comments = !_package.SettingsPage.EnableCommentCompletion,
+ }
+ };
+
+ GetCompletionsResponse? result = await RequestCommandAsync("GetCompletions", data, token);
+ return result != null ? result.completionItems : [];
+ }
+
+ public async Task AcceptCompletionAsync(string completionId)
+ {
+ AcceptCompletionRequest data = new()
+ {
+ metadata = GetMetadata(),
+ completion_id = completionId
+ };
+
+ await RequestCommandAsync("AcceptCompletion", data);
+ }
+
+ public async Task GetProcessesAsync()
+ {
+ return await RequestCommandAsync("GetProcesses", new { });
+ }
+
+ public Metadata GetMetadata()
+ {
+ return new()
+ {
+ request_id = _metadata.request_id++,
+ api_key = _metadata.api_key,
+ ide_name = _metadata.ide_name,
+ ide_version = _metadata.ide_version,
+ extension_name = _metadata.extension_name,
+ extension_version = _metadata.extension_version,
+ session_id = _metadata.session_id,
+ locale = _metadata.locale,
+ disable_telemetry = _metadata.disable_telemetry
+ };
+ }
}
\ No newline at end of file
diff --git a/CodeiumVS/LanguageServer/LanguageServerController.cs b/CodeiumVS/LanguageServer/LanguageServerController.cs
index 2612c22..a741641 100644
--- a/CodeiumVS/LanguageServer/LanguageServerController.cs
+++ b/CodeiumVS/LanguageServer/LanguageServerController.cs
@@ -352,7 +352,7 @@ static string RandomString(int length)
internal static bool Send(this WebServerRequest request, WebSocket ws)
{
- if (!ws.IsAlive)
+ if (ws == null || !ws.IsAlive)
{
CodeiumVSPackage.Instance.Log("Language Server Controller: Unable to send the request because the connection is closed.");
return false;
diff --git a/CodeiumVS/NotificationBar.cs b/CodeiumVS/NotificationBar.cs
index a3c595c..921d078 100644
--- a/CodeiumVS/NotificationBar.cs
+++ b/CodeiumVS/NotificationBar.cs
@@ -7,14 +7,17 @@
namespace CodeiumVS;
#nullable enable
-public class NotificationInfoBar : IVsInfoBarUIEvents
+public class NotificationInfoBar : IVsInfoBarUIEvents, IVsShellPropertyEvents
{
private IVsInfoBarUIElement? view;
private uint infoBarEventsCookie;
+ private uint shellPropertyEventsCookie;
+ private IVsShell? _vsShell;
private IVsInfoBarHost? vsInfoBarHost;
+ private IVsInfoBarUIFactory? _vsInfoBarFactory;
public bool IsShown { get; private set; }
@@ -22,6 +25,17 @@ public class NotificationInfoBar : IVsInfoBarUIEvents
public IVsInfoBarUIElement? View => view;
+ public static readonly KeyValuePair[] SupportActions = [
+ new KeyValuePair("Ask for support on Discord", delegate
+ {
+ CodeiumVSPackage.OpenInBrowser("https://discord.gg/3XFf78nAx5");
+ }),
+ new KeyValuePair("Report issue on GitHub", delegate
+ {
+ CodeiumVSPackage.OpenInBrowser("https://github.com/Exafunction/CodeiumVisualStudio/issues/new");
+ }),
+ ];
+
public NotificationInfoBar()
{
}
@@ -33,19 +47,27 @@ public void Show(string text, ImageMoniker? icon = null, bool canClose = true, A
try
{
- IVsShell vsShell = ServiceProvider.GlobalProvider.GetService();
- IVsInfoBarUIFactory vsInfoBarFactory = ServiceProvider.GlobalProvider.GetService();
- if (vsShell == null || vsInfoBarFactory == null) return;
-
- if (vsInfoBarFactory != null && ErrorHandler.Succeeded(vsShell.GetProperty((int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var pvar)) && pvar is IVsInfoBarHost vsInfoBarHost)
- {
- InfoBarModel infoBar = new(text, GetActionsItems(actions), icon ?? KnownMonikers.StatusInformation, canClose);
-
- view = vsInfoBarFactory.CreateInfoBar(infoBar);
- view.Advise(this, out infoBarEventsCookie);
+ _vsShell = ServiceProvider.GlobalProvider.GetService();
+ _vsInfoBarFactory = ServiceProvider.GlobalProvider.GetService();
+ if (_vsShell == null || _vsInfoBarFactory == null) return;
- this.vsInfoBarHost = vsInfoBarHost;
- this.vsInfoBarHost.AddInfoBar(view);
+ InfoBarModel infoBar = new(text, GetActionsItems(actions), icon ?? KnownMonikers.StatusInformation, canClose);
+
+ view = _vsInfoBarFactory.CreateInfoBar(infoBar);
+ view.Advise(this, out infoBarEventsCookie);
+
+ if (ErrorHandler.Succeeded(_vsShell.GetProperty((int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var pvar)))
+ {
+ if (pvar is IVsInfoBarHost vsInfoBarHost)
+ {
+ this.vsInfoBarHost = vsInfoBarHost;
+ this.vsInfoBarHost.AddInfoBar(view);
+ }
+ }
+ else
+ {
+ // the MainWindowInfoBarHost has not been created yet, so we delay showing the notification
+ _vsShell.AdviseShellPropertyChanges(this, out shellPropertyEventsCookie);
IsShown = true;
OnCloseCallback = onCloseCallback;
@@ -108,5 +130,24 @@ void IVsInfoBarUIEvents.OnActionItemClicked(IVsInfoBarUIElement infoBarUIElement
ThreadHelper.ThrowIfNotOnUIThread("OnActionItemClicked");
((Action)actionItem.ActionContext)();
}
+
+ public int OnShellPropertyChange(int propid, object var)
+ {
+ ThreadHelper.ThrowIfNotOnUIThread("OnShellPropertyChange");
+
+ //if (propid == (int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost) // for some reaons, this doesn't work
+ if (_vsShell?.GetProperty((int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var pvar) == VSConstants.S_OK)
+ {
+ _vsShell?.UnadviseShellPropertyChanges(shellPropertyEventsCookie);
+
+ if (pvar is IVsInfoBarHost vsInfoBarHost)
+ {
+ this.vsInfoBarHost = vsInfoBarHost;
+ this.vsInfoBarHost.AddInfoBar(view);
+ }
+ }
+
+ return VSConstants.S_OK;
+ }
}
#nullable disable
\ No newline at end of file
diff --git a/CodeiumVS/Utilities/FileUtilities.cs b/CodeiumVS/Utilities/FileUtilities.cs
new file mode 100644
index 0000000..c5890d4
--- /dev/null
+++ b/CodeiumVS/Utilities/FileUtilities.cs
@@ -0,0 +1,41 @@
+using System.IO;
+
+namespace CodeiumVS.Utilities;
+
+internal static class FileUtilities
+{
+ ///
+ /// Delete a file in a safe way, if the file is in use, rename it instead
+ ///
+ ///
+ internal static void DeleteSafe(string path)
+ {
+ try
+ {
+ File.Delete(path);
+ }
+ catch (Exception ex)
+ {
+ if (ex is UnauthorizedAccessException || ex is IOException)
+ {
+ // what's more beatiful than nested exceptions...
+ try
+ {
+ string orignalFileName = Path.GetFileName(path);
+ string randomPath = Path.Combine(Path.GetDirectoryName(path), orignalFileName + "_deleted_" + Path.GetRandomFileName());
+ File.Move(path, randomPath);
+ }
+ catch (Exception ex2)
+ {
+ CodeiumVSPackage.Instance?.Log($"Failed to move the file why trying to delete it, exception: {ex2}");
+ VS.MessageBox.ShowError($"Codeium: Failed to move the file why trying to delete it: {path}", "Please see the output windows for more details");
+ }
+ }
+ else
+ {
+ CodeiumVSPackage.Instance?.Log($"Failed to delete file, exception: {ex}");
+ VS.MessageBox.ShowError($"Codeium: Failed to delete file: {path}", "Please see the output windows for more details");
+ }
+ }
+ }
+}
diff --git a/CodeiumVS/Utilities/ProcessExtensions.cs b/CodeiumVS/Utilities/ProcessExtensions.cs
index 1491627..37abbf2 100644
--- a/CodeiumVS/Utilities/ProcessExtensions.cs
+++ b/CodeiumVS/Utilities/ProcessExtensions.cs
@@ -63,7 +63,7 @@ private struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
public UIntPtr PeakJobMemoryUsed;
}
- public static void MakeProcessExitOnParentExit(Process process)
+ public static bool MakeProcessExitOnParentExit(Process process)
{
IntPtr hJob = CreateJobObject(IntPtr.Zero, null);
@@ -76,10 +76,11 @@ public static void MakeProcessExitOnParentExit(Process process)
if (!SetInformationJobObject(hJob, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length))
{
- throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
+ return false;
}
AssignProcessToJobObject(hJob, process.Handle);
+ return true;
}
}
diff --git a/CodeiumVS/Windows/ChatToolWindow.cs b/CodeiumVS/Windows/ChatToolWindow.cs
index 9dc5081..fa17b7d 100644
--- a/CodeiumVS/Windows/ChatToolWindow.cs
+++ b/CodeiumVS/Windows/ChatToolWindow.cs
@@ -1,11 +1,9 @@
-using Community.VisualStudio.Toolkit;
-using Microsoft.VisualStudio;
+using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Imaging;
using Microsoft.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.Web.WebView2.Core;
using System.Collections.Generic;
-using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
@@ -349,6 +347,9 @@ private void VSColorTheme_ThemeChanged(ThemeChangedEventArgs e)
private void WebView_OnDOMContentLoaded(object sender, CoreWebView2DOMContentLoadedEventArgs e)
{
if (webView.Source.OriginalString != "about:blank")
+ {
_isChatPageLoaded = true;
+ _infoBar.Close();
+ }
}
}
diff --git a/CodeiumVS/Windows/EnterTokenDialogWindow.cs b/CodeiumVS/Windows/EnterTokenDialogWindow.cs
index bc7ddb7..85bcf63 100644
--- a/CodeiumVS/Windows/EnterTokenDialogWindow.cs
+++ b/CodeiumVS/Windows/EnterTokenDialogWindow.cs
@@ -51,6 +51,6 @@ private void HelpLinkClicked(object sender, System.Windows.Navigation.RequestNav
string redirectUrl = "show-auth-token";
string url = $"{portalUrl}/profile?response_type=token&redirect_uri={redirectUrl}&state={state}&scope=openid%20profile%20email&redirect_parameters_type=query";
- CodeiumVSPackage.Instance.OpenInBrowser(url);
+ CodeiumVSPackage.OpenInBrowser(url);
}
}
\ No newline at end of file