Detailed changes
@@ -1,3 +1,4 @@
+use futures::future::BoxFuture;
use gpui::AppContext;
#[derive(Clone, Debug)]
@@ -9,7 +10,14 @@ pub enum ProviderCredential {
pub trait CredentialProvider: Send + Sync {
fn has_credentials(&self) -> bool;
- fn retrieve_credentials(&self, cx: &mut AppContext) -> ProviderCredential;
- fn save_credentials(&self, cx: &mut AppContext, credential: ProviderCredential);
- fn delete_credentials(&self, cx: &mut AppContext);
+ #[must_use]
+ fn retrieve_credentials(&self, cx: &mut AppContext) -> BoxFuture<ProviderCredential>;
+ #[must_use]
+ fn save_credentials(
+ &self,
+ cx: &mut AppContext,
+ credential: ProviderCredential,
+ ) -> BoxFuture<()>;
+ #[must_use]
+ fn delete_credentials(&self, cx: &mut AppContext) -> BoxFuture<()>;
}
@@ -222,45 +222,70 @@ impl CredentialProvider for OpenAICompletionProvider {
}
}
- fn retrieve_credentials(&self, cx: &mut AppContext) -> ProviderCredential {
+ fn retrieve_credentials(&self, cx: &mut AppContext) -> BoxFuture<ProviderCredential> {
let existing_credential = self.credential.read().clone();
let retrieved_credential = match existing_credential {
- ProviderCredential::Credentials { .. } => existing_credential.clone(),
+ ProviderCredential::Credentials { .. } => {
+ return async move { existing_credential }.boxed()
+ }
_ => {
if let Some(api_key) = env::var("OPENAI_API_KEY").log_err() {
- ProviderCredential::Credentials { api_key }
- } else if let Some(Some((_, api_key))) =
- cx.read_credentials(OPENAI_API_URL).log_err()
- {
- if let Some(api_key) = String::from_utf8(api_key).log_err() {
- ProviderCredential::Credentials { api_key }
- } else {
- ProviderCredential::NoCredentials
- }
+ async move { ProviderCredential::Credentials { api_key } }.boxed()
} else {
- ProviderCredential::NoCredentials
+ let credentials = cx.read_credentials(OPENAI_API_URL);
+ async move {
+ if let Some(Some((_, api_key))) = credentials.await.log_err() {
+ if let Some(api_key) = String::from_utf8(api_key).log_err() {
+ ProviderCredential::Credentials { api_key }
+ } else {
+ ProviderCredential::NoCredentials
+ }
+ } else {
+ ProviderCredential::NoCredentials
+ }
+ }
+ .boxed()
}
}
};
- *self.credential.write() = retrieved_credential.clone();
- retrieved_credential
+
+ async move {
+ let retrieved_credential = retrieved_credential.await;
+ *self.credential.write() = retrieved_credential.clone();
+ retrieved_credential
+ }
+ .boxed()
}
- fn save_credentials(&self, cx: &mut AppContext, credential: ProviderCredential) {
+ fn save_credentials(
+ &self,
+ cx: &mut AppContext,
+ credential: ProviderCredential,
+ ) -> BoxFuture<()> {
*self.credential.write() = credential.clone();
let credential = credential.clone();
- match credential {
+ let write_credentials = match credential {
ProviderCredential::Credentials { api_key } => {
- cx.write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes())
- .log_err();
+ Some(cx.write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()))
+ }
+ _ => None,
+ };
+
+ async move {
+ if let Some(write_credentials) = write_credentials {
+ write_credentials.await.log_err();
}
- _ => {}
}
+ .boxed()
}
- fn delete_credentials(&self, cx: &mut AppContext) {
- cx.delete_credentials(OPENAI_API_URL).log_err();
+ fn delete_credentials(&self, cx: &mut AppContext) -> BoxFuture<()> {
*self.credential.write() = ProviderCredential::NoCredentials;
+ let delete_credentials = cx.delete_credentials(OPENAI_API_URL);
+ async move {
+ delete_credentials.await.log_err();
+ }
+ .boxed()
}
}
@@ -1,6 +1,8 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
+use futures::future::BoxFuture;
use futures::AsyncReadExt;
+use futures::FutureExt;
use gpui::AppContext;
use gpui::BackgroundExecutor;
use isahc::http::StatusCode;
@@ -157,46 +159,71 @@ impl CredentialProvider for OpenAIEmbeddingProvider {
_ => false,
}
}
- fn retrieve_credentials(&self, cx: &mut AppContext) -> ProviderCredential {
- let existing_credential = self.credential.read().clone();
+ fn retrieve_credentials(&self, cx: &mut AppContext) -> BoxFuture<ProviderCredential> {
+ let existing_credential = self.credential.read().clone();
let retrieved_credential = match existing_credential {
- ProviderCredential::Credentials { .. } => existing_credential.clone(),
+ ProviderCredential::Credentials { .. } => {
+ return async move { existing_credential }.boxed()
+ }
_ => {
if let Some(api_key) = env::var("OPENAI_API_KEY").log_err() {
- ProviderCredential::Credentials { api_key }
- } else if let Some(Some((_, api_key))) =
- cx.read_credentials(OPENAI_API_URL).log_err()
- {
- if let Some(api_key) = String::from_utf8(api_key).log_err() {
- ProviderCredential::Credentials { api_key }
- } else {
- ProviderCredential::NoCredentials
- }
+ async move { ProviderCredential::Credentials { api_key } }.boxed()
} else {
- ProviderCredential::NoCredentials
+ let credentials = cx.read_credentials(OPENAI_API_URL);
+ async move {
+ if let Some(Some((_, api_key))) = credentials.await.log_err() {
+ if let Some(api_key) = String::from_utf8(api_key).log_err() {
+ ProviderCredential::Credentials { api_key }
+ } else {
+ ProviderCredential::NoCredentials
+ }
+ } else {
+ ProviderCredential::NoCredentials
+ }
+ }
+ .boxed()
}
}
};
- *self.credential.write() = retrieved_credential.clone();
- retrieved_credential
+ async move {
+ let retrieved_credential = retrieved_credential.await;
+ *self.credential.write() = retrieved_credential.clone();
+ retrieved_credential
+ }
+ .boxed()
}
- fn save_credentials(&self, cx: &mut AppContext, credential: ProviderCredential) {
+ fn save_credentials(
+ &self,
+ cx: &mut AppContext,
+ credential: ProviderCredential,
+ ) -> BoxFuture<()> {
*self.credential.write() = credential.clone();
- match credential {
+ let credential = credential.clone();
+ let write_credentials = match credential {
ProviderCredential::Credentials { api_key } => {
- cx.write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes())
- .log_err();
+ Some(cx.write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()))
+ }
+ _ => None,
+ };
+
+ async move {
+ if let Some(write_credentials) = write_credentials {
+ write_credentials.await.log_err();
}
- _ => {}
}
+ .boxed()
}
- fn delete_credentials(&self, cx: &mut AppContext) {
- cx.delete_credentials(OPENAI_API_URL).log_err();
+ fn delete_credentials(&self, cx: &mut AppContext) -> BoxFuture<()> {
*self.credential.write() = ProviderCredential::NoCredentials;
+ let delete_credentials = cx.delete_credentials(OPENAI_API_URL);
+ async move {
+ delete_credentials.await.log_err();
+ }
+ .boxed()
}
}
@@ -104,11 +104,22 @@ impl CredentialProvider for FakeEmbeddingProvider {
fn has_credentials(&self) -> bool {
true
}
- fn retrieve_credentials(&self, _cx: &mut AppContext) -> ProviderCredential {
- ProviderCredential::NotNeeded
+
+ fn retrieve_credentials(&self, _cx: &mut AppContext) -> BoxFuture<ProviderCredential> {
+ async { ProviderCredential::NotNeeded }.boxed()
+ }
+
+ fn save_credentials(
+ &self,
+ _cx: &mut AppContext,
+ _credential: ProviderCredential,
+ ) -> BoxFuture<()> {
+ async {}.boxed()
+ }
+
+ fn delete_credentials(&self, _cx: &mut AppContext) -> BoxFuture<()> {
+ async {}.boxed()
}
- fn save_credentials(&self, _cx: &mut AppContext, _credential: ProviderCredential) {}
- fn delete_credentials(&self, _cx: &mut AppContext) {}
}
#[async_trait]
@@ -165,11 +176,22 @@ impl CredentialProvider for FakeCompletionProvider {
fn has_credentials(&self) -> bool {
true
}
- fn retrieve_credentials(&self, _cx: &mut AppContext) -> ProviderCredential {
- ProviderCredential::NotNeeded
+
+ fn retrieve_credentials(&self, _cx: &mut AppContext) -> BoxFuture<ProviderCredential> {
+ async { ProviderCredential::NotNeeded }.boxed()
+ }
+
+ fn save_credentials(
+ &self,
+ _cx: &mut AppContext,
+ _credential: ProviderCredential,
+ ) -> BoxFuture<()> {
+ async {}.boxed()
+ }
+
+ fn delete_credentials(&self, _cx: &mut AppContext) -> BoxFuture<()> {
+ async {}.boxed()
}
- fn save_credentials(&self, _cx: &mut AppContext, _credential: ProviderCredential) {}
- fn delete_credentials(&self, _cx: &mut AppContext) {}
}
impl CompletionProvider for FakeCompletionProvider {
@@ -6,14 +6,12 @@ use crate::{
NewConversation, QuoteSelection, ResetKey, Role, SavedConversation, SavedConversationMetadata,
SavedMessage, Split, ToggleFocus, ToggleIncludeConversation, ToggleRetrieveContext,
};
-
+use ai::prompts::repository_context::PromptCodeSnippet;
use ai::{
auth::ProviderCredential,
completion::{CompletionProvider, CompletionRequest},
providers::open_ai::{OpenAICompletionProvider, OpenAIRequest, RequestMessage},
};
-
-use ai::prompts::repository_context::PromptCodeSnippet;
use anyhow::{anyhow, Result};
use chrono::{DateTime, Local};
use client::telemetry::AssistantKind;
@@ -220,23 +218,9 @@ impl AssistantPanel {
_: &InlineAssist,
cx: &mut ViewContext<Workspace>,
) {
- let this = if let Some(this) = workspace.panel::<AssistantPanel>(cx) {
- if this.update(cx, |assistant, cx| {
- if !assistant.has_credentials() {
- assistant.load_credentials(cx);
- };
-
- assistant.has_credentials()
- }) {
- this
- } else {
- workspace.focus_panel::<AssistantPanel>(cx);
- return;
- }
- } else {
+ let Some(assistant) = workspace.panel::<AssistantPanel>(cx) else {
return;
};
-
let active_editor = if let Some(active_editor) = workspace
.active_item(cx)
.and_then(|item| item.act_as::<Editor>(cx))
@@ -245,12 +229,32 @@ impl AssistantPanel {
} else {
return;
};
+ let project = workspace.project().clone();
- let project = workspace.project();
+ if assistant.update(cx, |assistant, _| assistant.has_credentials()) {
+ assistant.update(cx, |assistant, cx| {
+ assistant.new_inline_assist(&active_editor, cx, &project)
+ });
+ } else {
+ let assistant = assistant.downgrade();
+ cx.spawn(|workspace, mut cx| async move {
+ assistant
+ .update(&mut cx, |assistant, cx| assistant.load_credentials(cx))?
+ .await;
+ if assistant.update(&mut cx, |assistant, _| assistant.has_credentials())? {
+ assistant.update(&mut cx, |assistant, cx| {
+ assistant.new_inline_assist(&active_editor, cx, &project)
+ })?;
+ } else {
+ workspace.update(&mut cx, |workspace, cx| {
+ workspace.focus_panel::<AssistantPanel>(cx)
+ })?;
+ }
- this.update(cx, |assistant, cx| {
- assistant.new_inline_assist(&active_editor, cx, project)
- });
+ anyhow::Ok(())
+ })
+ .detach_and_log_err(cx)
+ }
}
fn new_inline_assist(
@@ -290,9 +294,6 @@ impl AssistantPanel {
let inline_assist_id = post_inc(&mut self.next_inline_assist_id);
let provider = self.completion_provider.clone();
- // Retrieve Credentials Authenticates the Provider
- provider.retrieve_credentials(cx);
-
let codegen = cx.new_model(|cx| {
Codegen::new(editor.read(cx).buffer().clone(), codegen_kind, provider, cx)
});
@@ -845,11 +846,18 @@ impl AssistantPanel {
api_key: api_key.clone(),
};
- self.completion_provider.save_credentials(cx, credential);
+ let completion_provider = self.completion_provider.clone();
+ cx.spawn(|this, mut cx| async move {
+ cx.update(|cx| completion_provider.save_credentials(cx, credential))?
+ .await;
- self.api_key_editor.take();
- self.focus_handle.focus(cx);
- cx.notify();
+ this.update(&mut cx, |this, cx| {
+ this.api_key_editor.take();
+ this.focus_handle.focus(cx);
+ cx.notify();
+ })
+ })
+ .detach_and_log_err(cx);
}
} else {
cx.propagate();
@@ -857,10 +865,17 @@ impl AssistantPanel {
}
fn reset_credentials(&mut self, _: &ResetKey, cx: &mut ViewContext<Self>) {
- self.completion_provider.delete_credentials(cx);
- self.api_key_editor = Some(build_api_key_editor(cx));
- self.focus_handle.focus(cx);
- cx.notify();
+ let completion_provider = self.completion_provider.clone();
+ cx.spawn(|this, mut cx| async move {
+ cx.update(|cx| completion_provider.delete_credentials(cx))?
+ .await;
+ this.update(&mut cx, |this, cx| {
+ this.api_key_editor = Some(build_api_key_editor(cx));
+ this.focus_handle.focus(cx);
+ cx.notify();
+ })
+ })
+ .detach_and_log_err(cx);
}
fn toggle_zoom(&mut self, _: &workspace::ToggleZoom, cx: &mut ViewContext<Self>) {
@@ -1107,8 +1122,16 @@ impl AssistantPanel {
self.completion_provider.has_credentials()
}
- fn load_credentials(&mut self, cx: &mut ViewContext<Self>) {
- self.completion_provider.retrieve_credentials(cx);
+ fn load_credentials(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
+ let completion_provider = self.completion_provider.clone();
+ cx.spawn(|_, mut cx| async move {
+ if let Some(retrieve_credentials) = cx
+ .update(|cx| completion_provider.retrieve_credentials(cx))
+ .log_err()
+ {
+ retrieve_credentials.await;
+ }
+ })
}
}
@@ -1314,11 +1337,16 @@ impl Panel for AssistantPanel {
fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) {
if active {
- self.load_credentials(cx);
-
- if self.editors.is_empty() {
- self.new_conversation(cx);
- }
+ let load_credentials = self.load_credentials(cx);
+ cx.spawn(|this, mut cx| async move {
+ load_credentials.await;
+ this.update(&mut cx, |this, cx| {
+ if this.editors.is_empty() {
+ this.new_conversation(cx);
+ }
+ })
+ })
+ .detach_and_log_err(cx);
}
}
@@ -700,8 +700,8 @@ impl Client {
}
}
- pub fn has_keychain_credentials(&self, cx: &AsyncAppContext) -> bool {
- read_credentials_from_keychain(cx).is_some()
+ pub async fn has_keychain_credentials(&self, cx: &AsyncAppContext) -> bool {
+ read_credentials_from_keychain(cx).await.is_some()
}
#[async_recursion(?Send)]
@@ -732,7 +732,7 @@ impl Client {
let mut read_from_keychain = false;
let mut credentials = self.state.read().credentials.clone();
if credentials.is_none() && try_keychain {
- credentials = read_credentials_from_keychain(cx);
+ credentials = read_credentials_from_keychain(cx).await;
read_from_keychain = credentials.is_some();
}
if credentials.is_none() {
@@ -770,7 +770,7 @@ impl Client {
Ok(conn) => {
self.state.write().credentials = Some(credentials.clone());
if !read_from_keychain && IMPERSONATE_LOGIN.is_none() {
- write_credentials_to_keychain(credentials, cx).log_err();
+ write_credentials_to_keychain(credentials, cx).await.log_err();
}
futures::select_biased! {
@@ -784,7 +784,7 @@ impl Client {
Err(EstablishConnectionError::Unauthorized) => {
self.state.write().credentials.take();
if read_from_keychain {
- delete_credentials_from_keychain(cx).log_err();
+ delete_credentials_from_keychain(cx).await.log_err();
self.set_status(Status::SignedOut, cx);
self.authenticate_and_connect(false, cx).await
} else {
@@ -1350,14 +1350,16 @@ impl Client {
}
}
-fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option<Credentials> {
+async fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option<Credentials> {
if IMPERSONATE_LOGIN.is_some() {
return None;
}
let (user_id, access_token) = cx
- .update(|cx| cx.read_credentials(&ZED_SERVER_URL).log_err().flatten())
- .ok()??;
+ .update(|cx| cx.read_credentials(&ZED_SERVER_URL))
+ .log_err()?
+ .await
+ .log_err()??;
Some(Credentials {
user_id: user_id.parse().ok()?,
@@ -1365,7 +1367,10 @@ fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option<Credentials> {
})
}
-fn write_credentials_to_keychain(credentials: Credentials, cx: &AsyncAppContext) -> Result<()> {
+async fn write_credentials_to_keychain(
+ credentials: Credentials,
+ cx: &AsyncAppContext,
+) -> Result<()> {
cx.update(move |cx| {
cx.write_credentials(
&ZED_SERVER_URL,
@@ -1373,10 +1378,12 @@ fn write_credentials_to_keychain(credentials: Credentials, cx: &AsyncAppContext)
credentials.access_token.as_bytes(),
)
})?
+ .await
}
-fn delete_credentials_from_keychain(cx: &AsyncAppContext) -> Result<()> {
+async fn delete_credentials_from_keychain(cx: &AsyncAppContext) -> Result<()> {
cx.update(move |cx| cx.delete_credentials(&ZED_SERVER_URL))?
+ .await
}
const WORKTREE_URL_PREFIX: &str = "zed://worktrees/";
@@ -523,17 +523,22 @@ impl AppContext {
}
/// Writes credentials to the platform keychain.
- pub fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> {
+ pub fn write_credentials(
+ &self,
+ url: &str,
+ username: &str,
+ password: &[u8],
+ ) -> Task<Result<()>> {
self.platform.write_credentials(url, username, password)
}
/// Reads credentials from the platform keychain.
- pub fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>> {
+ pub fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
self.platform.read_credentials(url)
}
/// Deletes credentials from the platform keychain.
- pub fn delete_credentials(&self, url: &str) -> Result<()> {
+ pub fn delete_credentials(&self, url: &str) -> Task<Result<()>> {
self.platform.delete_credentials(url)
}
@@ -9,7 +9,7 @@ use crate::{
Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels, Font,
FontId, FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout,
Pixels, PlatformInput, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result,
- Scene, SharedString, Size, TaskLabel, WindowContext,
+ Scene, SharedString, Size, Task, TaskLabel, WindowContext,
};
use anyhow::anyhow;
use async_task::Runnable;
@@ -108,9 +108,9 @@ pub(crate) trait Platform: 'static {
fn write_to_clipboard(&self, item: ClipboardItem);
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
- fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()>;
- fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>>;
- fn delete_credentials(&self, url: &str) -> Result<()>;
+ fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>>;
+ fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>>;
+ fn delete_credentials(&self, url: &str) -> Task<Result<()>>;
}
/// A handle to a platform's display, e.g. a monitor or laptop screen.
@@ -3,7 +3,7 @@ use crate::{
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
ForegroundExecutor, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker, MacTextSystem,
MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay, PlatformInput,
- PlatformTextSystem, PlatformWindow, Result, SemanticVersion, WindowOptions,
+ PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task, WindowOptions,
};
use anyhow::anyhow;
use block::ConcreteBlock;
@@ -856,104 +856,115 @@ impl Platform for MacPlatform {
}
}
- fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> {
- let url = CFString::from(url);
- let username = CFString::from(username);
- let password = CFData::from_buffer(password);
-
- unsafe {
- use security::*;
-
- // First, check if there are already credentials for the given server. If so, then
- // update the username and password.
- let mut verb = "updating";
- let mut query_attrs = CFMutableDictionary::with_capacity(2);
- query_attrs.set(kSecClass as *const _, kSecClassInternetPassword as *const _);
- query_attrs.set(kSecAttrServer as *const _, url.as_CFTypeRef());
-
- let mut attrs = CFMutableDictionary::with_capacity(4);
- attrs.set(kSecClass as *const _, kSecClassInternetPassword as *const _);
- attrs.set(kSecAttrServer as *const _, url.as_CFTypeRef());
- attrs.set(kSecAttrAccount as *const _, username.as_CFTypeRef());
- attrs.set(kSecValueData as *const _, password.as_CFTypeRef());
-
- let mut status = SecItemUpdate(
- query_attrs.as_concrete_TypeRef(),
- attrs.as_concrete_TypeRef(),
- );
+ fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>> {
+ let url = url.to_string();
+ let username = username.to_string();
+ let password = password.to_vec();
+ self.background_executor().spawn(async move {
+ unsafe {
+ use security::*;
+
+ let url = CFString::from(url.as_str());
+ let username = CFString::from(username.as_str());
+ let password = CFData::from_buffer(&password);
+
+ // First, check if there are already credentials for the given server. If so, then
+ // update the username and password.
+ let mut verb = "updating";
+ let mut query_attrs = CFMutableDictionary::with_capacity(2);
+ query_attrs.set(kSecClass as *const _, kSecClassInternetPassword as *const _);
+ query_attrs.set(kSecAttrServer as *const _, url.as_CFTypeRef());
+
+ let mut attrs = CFMutableDictionary::with_capacity(4);
+ attrs.set(kSecClass as *const _, kSecClassInternetPassword as *const _);
+ attrs.set(kSecAttrServer as *const _, url.as_CFTypeRef());
+ attrs.set(kSecAttrAccount as *const _, username.as_CFTypeRef());
+ attrs.set(kSecValueData as *const _, password.as_CFTypeRef());
+
+ let mut status = SecItemUpdate(
+ query_attrs.as_concrete_TypeRef(),
+ attrs.as_concrete_TypeRef(),
+ );
- // If there were no existing credentials for the given server, then create them.
- if status == errSecItemNotFound {
- verb = "creating";
- status = SecItemAdd(attrs.as_concrete_TypeRef(), ptr::null_mut());
- }
+ // If there were no existing credentials for the given server, then create them.
+ if status == errSecItemNotFound {
+ verb = "creating";
+ status = SecItemAdd(attrs.as_concrete_TypeRef(), ptr::null_mut());
+ }
- if status != errSecSuccess {
- return Err(anyhow!("{} password failed: {}", verb, status));
+ if status != errSecSuccess {
+ return Err(anyhow!("{} password failed: {}", verb, status));
+ }
}
- }
- Ok(())
- }
-
- fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>> {
- let url = CFString::from(url);
- let cf_true = CFBoolean::true_value().as_CFTypeRef();
+ Ok(())
+ })
+ }
+
+ fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
+ let url = url.to_string();
+ self.background_executor().spawn(async move {
+ let url = CFString::from(url.as_str());
+ let cf_true = CFBoolean::true_value().as_CFTypeRef();
+
+ unsafe {
+ use security::*;
+
+ // Find any credentials for the given server URL.
+ let mut attrs = CFMutableDictionary::with_capacity(5);
+ attrs.set(kSecClass as *const _, kSecClassInternetPassword as *const _);
+ attrs.set(kSecAttrServer as *const _, url.as_CFTypeRef());
+ attrs.set(kSecReturnAttributes as *const _, cf_true);
+ attrs.set(kSecReturnData as *const _, cf_true);
+
+ let mut result = CFTypeRef::from(ptr::null());
+ let status = SecItemCopyMatching(attrs.as_concrete_TypeRef(), &mut result);
+ match status {
+ security::errSecSuccess => {}
+ security::errSecItemNotFound | security::errSecUserCanceled => return Ok(None),
+ _ => return Err(anyhow!("reading password failed: {}", status)),
+ }
- unsafe {
- use security::*;
-
- // Find any credentials for the given server URL.
- let mut attrs = CFMutableDictionary::with_capacity(5);
- attrs.set(kSecClass as *const _, kSecClassInternetPassword as *const _);
- attrs.set(kSecAttrServer as *const _, url.as_CFTypeRef());
- attrs.set(kSecReturnAttributes as *const _, cf_true);
- attrs.set(kSecReturnData as *const _, cf_true);
-
- let mut result = CFTypeRef::from(ptr::null());
- let status = SecItemCopyMatching(attrs.as_concrete_TypeRef(), &mut result);
- match status {
- security::errSecSuccess => {}
- security::errSecItemNotFound | security::errSecUserCanceled => return Ok(None),
- _ => return Err(anyhow!("reading password failed: {}", status)),
+ let result = CFType::wrap_under_create_rule(result)
+ .downcast::<CFDictionary>()
+ .ok_or_else(|| anyhow!("keychain item was not a dictionary"))?;
+ let username = result
+ .find(kSecAttrAccount as *const _)
+ .ok_or_else(|| anyhow!("account was missing from keychain item"))?;
+ let username = CFType::wrap_under_get_rule(*username)
+ .downcast::<CFString>()
+ .ok_or_else(|| anyhow!("account was not a string"))?;
+ let password = result
+ .find(kSecValueData as *const _)
+ .ok_or_else(|| anyhow!("password was missing from keychain item"))?;
+ let password = CFType::wrap_under_get_rule(*password)
+ .downcast::<CFData>()
+ .ok_or_else(|| anyhow!("password was not a string"))?;
+
+ Ok(Some((username.to_string(), password.bytes().to_vec())))
}
-
- let result = CFType::wrap_under_create_rule(result)
- .downcast::<CFDictionary>()
- .ok_or_else(|| anyhow!("keychain item was not a dictionary"))?;
- let username = result
- .find(kSecAttrAccount as *const _)
- .ok_or_else(|| anyhow!("account was missing from keychain item"))?;
- let username = CFType::wrap_under_get_rule(*username)
- .downcast::<CFString>()
- .ok_or_else(|| anyhow!("account was not a string"))?;
- let password = result
- .find(kSecValueData as *const _)
- .ok_or_else(|| anyhow!("password was missing from keychain item"))?;
- let password = CFType::wrap_under_get_rule(*password)
- .downcast::<CFData>()
- .ok_or_else(|| anyhow!("password was not a string"))?;
-
- Ok(Some((username.to_string(), password.bytes().to_vec())))
- }
+ })
}
- fn delete_credentials(&self, url: &str) -> Result<()> {
- let url = CFString::from(url);
+ fn delete_credentials(&self, url: &str) -> Task<Result<()>> {
+ let url = url.to_string();
- unsafe {
- use security::*;
+ self.background_executor().spawn(async move {
+ unsafe {
+ use security::*;
- let mut query_attrs = CFMutableDictionary::with_capacity(2);
- query_attrs.set(kSecClass as *const _, kSecClassInternetPassword as *const _);
- query_attrs.set(kSecAttrServer as *const _, url.as_CFTypeRef());
+ let url = CFString::from(url.as_str());
+ let mut query_attrs = CFMutableDictionary::with_capacity(2);
+ query_attrs.set(kSecClass as *const _, kSecClassInternetPassword as *const _);
+ query_attrs.set(kSecAttrServer as *const _, url.as_CFTypeRef());
- let status = SecItemDelete(query_attrs.as_concrete_TypeRef());
+ let status = SecItemDelete(query_attrs.as_concrete_TypeRef());
- if status != errSecSuccess {
- return Err(anyhow!("delete password failed: {}", status));
+ if status != errSecSuccess {
+ return Err(anyhow!("delete password failed: {}", status));
+ }
}
- }
- Ok(())
+ Ok(())
+ })
}
}
@@ -1,6 +1,7 @@
use crate::{
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor,
- Keymap, Platform, PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow, WindowOptions,
+ Keymap, Platform, PlatformDisplay, PlatformTextSystem, Task, TestDisplay, TestWindow,
+ WindowOptions,
};
use anyhow::{anyhow, Result};
use collections::VecDeque;
@@ -280,16 +281,16 @@ impl Platform for TestPlatform {
self.current_clipboard_item.lock().clone()
}
- fn write_credentials(&self, _url: &str, _username: &str, _password: &[u8]) -> Result<()> {
- Ok(())
+ fn write_credentials(&self, _url: &str, _username: &str, _password: &[u8]) -> Task<Result<()>> {
+ Task::ready(Ok(()))
}
- fn read_credentials(&self, _url: &str) -> Result<Option<(String, Vec<u8>)>> {
- Ok(None)
+ fn read_credentials(&self, _url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
+ Task::ready(Ok(None))
}
- fn delete_credentials(&self, _url: &str) -> Result<()> {
- Ok(())
+ fn delete_credentials(&self, _url: &str) -> Task<Result<()>> {
+ Task::ready(Ok(()))
}
fn double_click_interval(&self) -> std::time::Duration {
@@ -278,14 +278,22 @@ impl SemanticIndex {
.map(|semantic_index| semantic_index.clone())
}
- pub fn authenticate(&mut self, cx: &mut AppContext) -> bool {
+ pub fn authenticate(&mut self, cx: &mut AppContext) -> Task<bool> {
if !self.embedding_provider.has_credentials() {
- self.embedding_provider.retrieve_credentials(cx);
+ let embedding_provider = self.embedding_provider.clone();
+ cx.spawn(|cx| async move {
+ if let Some(retrieve_credentials) = cx
+ .update(|cx| embedding_provider.retrieve_credentials(cx))
+ .log_err()
+ {
+ retrieve_credentials.await;
+ }
+
+ embedding_provider.has_credentials()
+ })
} else {
- return true;
+ Task::ready(true)
}
-
- self.embedding_provider.has_credentials()
}
pub fn is_authenticated(&self) -> bool {
@@ -1005,12 +1013,26 @@ impl SemanticIndex {
project: Model<Project>,
cx: &mut ModelContext<Self>,
) -> Task<Result<()>> {
- if !self.is_authenticated() {
- if !self.authenticate(cx) {
- return Task::ready(Err(anyhow!("user is not authenticated")));
- }
+ if self.is_authenticated() {
+ self.index_project_internal(project, cx)
+ } else {
+ let authenticate = self.authenticate(cx);
+ cx.spawn(|this, mut cx| async move {
+ if authenticate.await {
+ this.update(&mut cx, |this, cx| this.index_project_internal(project, cx))?
+ .await
+ } else {
+ Err(anyhow!("user is not authenticated"))
+ }
+ })
}
+ }
+ fn index_project_internal(
+ &mut self,
+ project: Model<Project>,
+ cx: &mut ModelContext<Self>,
+ ) -> Task<Result<()>> {
if !self.projects.contains_key(&project.downgrade()) {
let subscription = cx.subscribe(&project, |this, project, event, cx| match event {
project::Event::WorktreeAdded | project::Event::WorktreeRemoved(_) => {
@@ -376,7 +376,7 @@ async fn authenticate(client: Arc<Client>, cx: &AsyncAppContext) -> Result<()> {
if client::IMPERSONATE_LOGIN.is_some() {
client.authenticate_and_connect(false, &cx).await?;
}
- } else if client.has_keychain_credentials(&cx) {
+ } else if client.has_keychain_credentials(&cx).await {
client.authenticate_and_connect(true, &cx).await?;
}
Ok::<_, anyhow::Error>(())