diff --git a/crates/language_models/src/provider/anthropic.rs b/crates/language_models/src/provider/anthropic.rs index f9c664182426dfd9523254867ba2f95b8f3dc7c4..e35787b501d74016ce214ac6823ca147e22f6c14 100644 --- a/crates/language_models/src/provider/anthropic.rs +++ b/crates/language_models/src/provider/anthropic.rs @@ -1,14 +1,13 @@ +use crate::api_key::ApiKeyState; use crate::ui::InstructionListItem; -use crate::{AllLanguageModelSettings, api_key::ApiKeyState}; use anthropic::{ AnthropicError, AnthropicModelMode, ContentDelta, Event, ResponseContent, ToolResultContent, ToolResultPart, Usage, }; -use anyhow::Result; +use anyhow::{Result, anyhow}; use collections::{BTreeMap, HashMap}; use editor::{Editor, EditorElement, EditorStyle}; -use futures::Stream; -use futures::{FutureExt, StreamExt, future::BoxFuture, stream::BoxStream}; +use futures::{FutureExt, Stream, StreamExt, future, future::BoxFuture, stream::BoxStream}; use gpui::{AnyView, App, AsyncApp, Context, Entity, FontStyle, Task, TextStyle, WhiteSpace}; use http_client::HttpClient; use language_model::{ @@ -157,7 +156,7 @@ impl AnthropicLanguageModelProvider { } fn settings(cx: &App) -> &AnthropicSettings { - &AllLanguageModelSettings::get_global(cx).anthropic + &crate::AllLanguageModelSettings::get_global(cx).anthropic } fn api_url(cx: &App) -> SharedString { @@ -220,11 +219,7 @@ impl LanguageModelProvider for AnthropicLanguageModelProvider { } // Override with available models from settings - for model in AllLanguageModelSettings::get_global(cx) - .anthropic - .available_models - .iter() - { + for model in &AnthropicLanguageModelProvider::settings(cx).available_models { models.insert( model.name.clone(), anthropic::Model::Custom { @@ -363,16 +358,11 @@ impl AnthropicModel { > { let http_client = self.http_client.clone(); - let api_key_and_url = self.state.read_with(cx, |state, cx| { + let Ok((api_key, api_url)) = self.state.read_with(cx, |state, cx| { let api_url = AnthropicLanguageModelProvider::api_url(cx); - let api_key = state.api_key_state.key(&api_url); - (api_key, api_url) - }); - let (api_key, api_url) = match api_key_and_url { - Ok(api_key_and_url) => api_key_and_url, - Err(err) => { - return futures::future::ready(Err(err.into())).boxed(); - } + (state.api_key_state.key(&api_url), api_url) + }) else { + return future::ready(Err(anyhow!("App state dropped").into())).boxed(); }; let beta_headers = self.model.beta_headers(); @@ -938,6 +928,10 @@ impl ConfigurationView { return; } + // url changes can cause the editor to be displayed again + self.api_key_editor + .update(cx, |editor, cx| editor.set_text("", window, cx)); + let state = self.state.clone(); cx.spawn_in(window, async move |_, cx| { state diff --git a/crates/language_models/src/provider/deepseek.rs b/crates/language_models/src/provider/deepseek.rs index e00d8bbf4b34560bdc55c982114bf96675e22d99..8bd8f4d2ed811e3a481d35b27f32bffc77539c31 100644 --- a/crates/language_models/src/provider/deepseek.rs +++ b/crates/language_models/src/provider/deepseek.rs @@ -2,7 +2,7 @@ use anyhow::{Result, anyhow}; use collections::{BTreeMap, HashMap}; use editor::{Editor, EditorElement, EditorStyle}; use futures::Stream; -use futures::{FutureExt, StreamExt, future::BoxFuture, stream::BoxStream}; +use futures::{FutureExt, StreamExt, future, future::BoxFuture, stream::BoxStream}; use gpui::{ AnyView, App, AsyncApp, Context, Entity, FontStyle, SharedString, Task, TextStyle, WhiteSpace, Window, @@ -26,7 +26,7 @@ use ui::{Icon, IconName, List, prelude::*}; use util::ResultExt; use zed_env_vars::{EnvVar, env_var}; -use crate::{AllLanguageModelSettings, api_key::ApiKeyState, ui::InstructionListItem}; +use crate::{api_key::ApiKeyState, ui::InstructionListItem}; const PROVIDER_ID: LanguageModelProviderId = LanguageModelProviderId::new("deepseek"); const PROVIDER_NAME: LanguageModelProviderName = LanguageModelProviderName::new("DeepSeek"); @@ -119,7 +119,7 @@ impl DeepSeekLanguageModelProvider { } fn settings(cx: &App) -> &DeepSeekSettings { - &AllLanguageModelSettings::get_global(cx).deepseek + &crate::AllLanguageModelSettings::get_global(cx).deepseek } fn api_url(cx: &App) -> &str { @@ -220,16 +220,11 @@ impl DeepSeekLanguageModel { ) -> BoxFuture<'static, Result>>> { let http_client = self.http_client.clone(); - let api_key_and_url = self.state.read_with(cx, |state, cx| { + let Ok((api_key, api_url)) = self.state.read_with(cx, |state, cx| { let api_url = DeepSeekLanguageModelProvider::api_url(cx); - let api_key = state.api_key_state.key(api_url); - (api_key, api_url.to_string()) - }); - let (api_key, api_url) = match api_key_and_url { - Ok(api_key_and_url) => api_key_and_url, - Err(err) => { - return futures::future::ready(Err(err)).boxed(); - } + (state.api_key_state.key(api_url), api_url.to_string()) + }) else { + return future::ready(Err(anyhow!("App state dropped"))).boxed(); }; let future = self.request_limiter.stream(async move { diff --git a/crates/language_models/src/provider/google.rs b/crates/language_models/src/provider/google.rs index 677d5775b3854245ad441acf37b0e1ed02f6bab5..70b028bfecaeef38468ced2814f40dfe8cb4103a 100644 --- a/crates/language_models/src/provider/google.rs +++ b/crates/language_models/src/provider/google.rs @@ -1,8 +1,8 @@ -use anyhow::{Context as _, Result}; +use anyhow::{Context as _, Result, anyhow}; use collections::BTreeMap; use credentials_provider::CredentialsProvider; use editor::{Editor, EditorElement, EditorStyle}; -use futures::{FutureExt, Stream, StreamExt, future::BoxFuture}; +use futures::{FutureExt, Stream, StreamExt, future, future::BoxFuture}; use google_ai::{ FunctionDeclaration, GenerateContentResponse, GoogleModelMode, Part, SystemInstruction, ThinkingConfig, UsageMetadata, @@ -37,8 +37,8 @@ use util::ResultExt; use zed_env_vars::EnvVar; use crate::api_key::ApiKey; +use crate::api_key::ApiKeyState; use crate::ui::InstructionListItem; -use crate::{AllLanguageModelSettings, api_key::ApiKeyState}; const PROVIDER_ID: LanguageModelProviderId = language_model::GOOGLE_PROVIDER_ID; const PROVIDER_NAME: LanguageModelProviderName = language_model::GOOGLE_PROVIDER_NAME; @@ -173,8 +173,12 @@ impl GoogleLanguageModelProvider { }) } + fn settings(cx: &App) -> &GoogleSettings { + &crate::AllLanguageModelSettings::get_global(cx).google + } + fn api_url(cx: &App) -> SharedString { - let api_url = &AllLanguageModelSettings::get_global(cx).google.api_url; + let api_url = &Self::settings(cx).api_url; if api_url.is_empty() { google_ai::API_URL.into() } else { @@ -223,10 +227,7 @@ impl LanguageModelProvider for GoogleLanguageModelProvider { } // Override with available models from settings - for model in &AllLanguageModelSettings::get_global(cx) - .google - .available_models - { + for model in &GoogleLanguageModelProvider::settings(cx).available_models { models.insert( model.name.clone(), google_ai::Model::Custom { @@ -295,16 +296,11 @@ impl GoogleLanguageModel { > { let http_client = self.http_client.clone(); - let api_key_and_url = self.state.read_with(cx, |state, cx| { + let Ok((api_key, api_url)) = self.state.read_with(cx, |state, cx| { let api_url = GoogleLanguageModelProvider::api_url(cx); - let api_key = state.api_key_state.key(&api_url); - (api_key, api_url) - }); - let (api_key, api_url) = match api_key_and_url { - Ok(api_key_and_url) => api_key_and_url, - Err(err) => { - return futures::future::ready(Err(err)).boxed(); - } + (state.api_key_state.key(&api_url), api_url) + }) else { + return future::ready(Err(anyhow!("App state dropped"))).boxed(); }; async move { @@ -820,6 +816,10 @@ impl ConfigurationView { return; } + // url changes can cause the editor to be displayed again + self.api_key_editor + .update(cx, |editor, cx| editor.set_text("", window, cx)); + let state = self.state.clone(); cx.spawn_in(window, async move |_, cx| { state diff --git a/crates/language_models/src/provider/mistral.rs b/crates/language_models/src/provider/mistral.rs index d2de056489d5fe10bab5eaa41780c0a5f312a181..260e438efacb1d9db165f8a67107dc412394c8de 100644 --- a/crates/language_models/src/provider/mistral.rs +++ b/crates/language_models/src/provider/mistral.rs @@ -1,7 +1,7 @@ use anyhow::{Result, anyhow}; use collections::BTreeMap; use editor::{Editor, EditorElement, EditorStyle}; -use futures::{FutureExt, Stream, StreamExt, future::BoxFuture, stream::BoxStream}; +use futures::{FutureExt, Stream, StreamExt, future, future::BoxFuture, stream::BoxStream}; use gpui::{ AnyView, App, AsyncApp, Context, Entity, FontStyle, SharedString, Task, TextStyle, WhiteSpace, Window, @@ -244,16 +244,11 @@ impl MistralLanguageModel { > { let http_client = self.http_client.clone(); - let api_key_and_url = self.state.read_with(cx, |state, cx| { + let Ok((api_key, api_url)) = self.state.read_with(cx, |state, cx| { let api_url = MistralLanguageModelProvider::api_url(cx); - let api_key = state.api_key_state.key(&api_url); - (api_key, api_url) - }); - let (api_key, api_url) = match api_key_and_url { - Ok(api_key_and_url) => api_key_and_url, - Err(err) => { - return futures::future::ready(Err(err)).boxed(); - } + (state.api_key_state.key(&api_url), api_url) + }) else { + return future::ready(Err(anyhow!("App state dropped"))).boxed(); }; let future = self.request_limiter.stream(async move { @@ -762,6 +757,10 @@ impl ConfigurationView { return; } + // url changes can cause the editor to be displayed again + self.api_key_editor + .update(cx, |editor, cx| editor.set_text("", window, cx)); + let state = self.state.clone(); cx.spawn_in(window, async move |_, cx| { state diff --git a/crates/language_models/src/provider/open_ai.rs b/crates/language_models/src/provider/open_ai.rs index a7e8f64d6614d95e4e892aba569d441320108ca4..050ddd09ab9da5f93cedec8ad204b0b8d6d726c3 100644 --- a/crates/language_models/src/provider/open_ai.rs +++ b/crates/language_models/src/provider/open_ai.rs @@ -1,7 +1,7 @@ use anyhow::{Result, anyhow}; use collections::{BTreeMap, HashMap}; use futures::Stream; -use futures::{FutureExt, StreamExt, future::BoxFuture}; +use futures::{FutureExt, StreamExt, future, future::BoxFuture}; use gpui::{AnyView, App, AsyncApp, Context, Entity, SharedString, Task, Window}; use http_client::HttpClient; use language_model::{ @@ -25,7 +25,7 @@ use ui_input::SingleLineInput; use util::ResultExt; use zed_env_vars::{EnvVar, env_var}; -use crate::{AllLanguageModelSettings, api_key::ApiKeyState, ui::InstructionListItem}; +use crate::{api_key::ApiKeyState, ui::InstructionListItem}; const PROVIDER_ID: LanguageModelProviderId = language_model::OPEN_AI_PROVIDER_ID; const PROVIDER_NAME: LanguageModelProviderName = language_model::OPEN_AI_PROVIDER_NAME; @@ -113,7 +113,7 @@ impl OpenAiLanguageModelProvider { } fn settings(cx: &App) -> &OpenAiSettings { - &AllLanguageModelSettings::get_global(cx).openai + &crate::AllLanguageModelSettings::get_global(cx).openai } fn api_url(cx: &App) -> SharedString { @@ -166,10 +166,7 @@ impl LanguageModelProvider for OpenAiLanguageModelProvider { } // Override with available models from settings - for model in &AllLanguageModelSettings::get_global(cx) - .openai - .available_models - { + for model in &OpenAiLanguageModelProvider::settings(cx).available_models { models.insert( model.name.clone(), open_ai::Model::Custom { @@ -230,16 +227,11 @@ impl OpenAiLanguageModel { { let http_client = self.http_client.clone(); - let api_key_and_url = self.state.read_with(cx, |state, cx| { + let Ok((api_key, api_url)) = self.state.read_with(cx, |state, cx| { let api_url = OpenAiLanguageModelProvider::api_url(cx); - let api_key = state.api_key_state.key(&api_url); - (api_key, api_url) - }); - let (api_key, api_url) = match api_key_and_url { - Ok(api_key_and_url) => api_key_and_url, - Err(err) => { - return futures::future::ready(Err(err)).boxed(); - } + (state.api_key_state.key(&api_url), api_url) + }) else { + return future::ready(Err(anyhow!("App state dropped"))).boxed(); }; let future = self.request_limiter.stream(async move { @@ -744,6 +736,10 @@ impl ConfigurationView { return; } + // url changes can cause the editor to be displayed again + self.api_key_editor + .update(cx, |editor, cx| editor.set_text("", window, cx)); + let state = self.state.clone(); cx.spawn_in(window, async move |_, cx| { state @@ -754,11 +750,8 @@ impl ConfigurationView { } fn reset_api_key(&mut self, window: &mut Window, cx: &mut Context) { - self.api_key_editor.update(cx, |input, cx| { - input.editor.update(cx, |editor, cx| { - editor.set_text("", window, cx); - }); - }); + self.api_key_editor + .update(cx, |input, cx| input.set_text("", window, cx)); let state = self.state.clone(); cx.spawn_in(window, async move |_, cx| { diff --git a/crates/language_models/src/provider/open_ai_compatible.rs b/crates/language_models/src/provider/open_ai_compatible.rs index 2b5e372a1d6ccd9b3bb13feb777f1091f0336b58..83cbaddb7076c6665dc82300cc71b8b8d037f1fb 100644 --- a/crates/language_models/src/provider/open_ai_compatible.rs +++ b/crates/language_models/src/provider/open_ai_compatible.rs @@ -1,6 +1,6 @@ -use anyhow::Result; +use anyhow::{Result, anyhow}; use convert_case::{Case, Casing}; -use futures::{FutureExt, StreamExt, future::BoxFuture}; +use futures::{FutureExt, StreamExt, future, future::BoxFuture}; use gpui::{AnyView, App, AsyncApp, Context, Entity, SharedString, Task, Window}; use http_client::HttpClient; use language_model::{ @@ -20,8 +20,8 @@ use ui_input::SingleLineInput; use util::ResultExt; use zed_env_vars::EnvVar; +use crate::api_key::ApiKeyState; use crate::provider::open_ai::{OpenAiEventMapper, into_open_ai}; -use crate::{AllLanguageModelSettings, api_key::ApiKeyState}; #[derive(Default, Clone, Debug, PartialEq)] pub struct OpenAiCompatibleSettings { @@ -98,7 +98,7 @@ impl State { impl OpenAiCompatibleLanguageModelProvider { pub fn new(id: Arc, http_client: Arc, cx: &mut App) -> Self { fn resolve_settings<'a>(id: &'a str, cx: &'a App) -> Option<&'a OpenAiCompatibleSettings> { - AllLanguageModelSettings::get_global(cx) + crate::AllLanguageModelSettings::get_global(cx) .openai_compatible .get(id) } @@ -239,16 +239,14 @@ impl OpenAiCompatibleLanguageModel { { let http_client = self.http_client.clone(); - let api_key_and_url = self.state.read_with(cx, |state, _cx| { + let Ok((api_key, api_url)) = self.state.read_with(cx, |state, _cx| { let api_url = &state.settings.api_url; - let api_key = state.api_key_state.key(api_url); - (api_key, state.settings.api_url.clone()) - }); - let (api_key, api_url) = match api_key_and_url { - Ok(api_key_and_url) => api_key_and_url, - Err(err) => { - return futures::future::ready(Err(err)).boxed(); - } + ( + state.api_key_state.key(api_url), + state.settings.api_url.clone(), + ) + }) else { + return future::ready(Err(anyhow!("App state dropped"))).boxed(); }; let provider = self.provider_name.clone(); @@ -423,6 +421,10 @@ impl ConfigurationView { return; } + // url changes can cause the editor to be displayed again + self.api_key_editor + .update(cx, |input, cx| input.set_text("", window, cx)); + let state = self.state.clone(); cx.spawn_in(window, async move |_, cx| { state @@ -433,11 +435,8 @@ impl ConfigurationView { } fn reset_api_key(&mut self, window: &mut Window, cx: &mut Context) { - self.api_key_editor.update(cx, |input, cx| { - input.editor.update(cx, |editor, cx| { - editor.set_text("", window, cx); - }); - }); + self.api_key_editor + .update(cx, |input, cx| input.set_text("", window, cx)); let state = self.state.clone(); cx.spawn_in(window, async move |_, cx| { diff --git a/crates/language_models/src/provider/open_router.rs b/crates/language_models/src/provider/open_router.rs index efa6a1eabe33fe8ecb52da3b5c61122cfc26e036..116f40e3ad4490f63e27d82ea08abec2407f407a 100644 --- a/crates/language_models/src/provider/open_router.rs +++ b/crates/language_models/src/provider/open_router.rs @@ -1,7 +1,7 @@ use anyhow::{Result, anyhow}; use collections::HashMap; use editor::{Editor, EditorElement, EditorStyle}; -use futures::{FutureExt, Stream, StreamExt, future::BoxFuture}; +use futures::{FutureExt, Stream, StreamExt, future, future::BoxFuture}; use gpui::{ AnyView, App, AsyncApp, Context, Entity, FontStyle, SharedString, Task, TextStyle, WhiteSpace, }; @@ -27,7 +27,7 @@ use ui::{Icon, IconName, List, Tooltip, prelude::*}; use util::ResultExt; use zed_env_vars::{EnvVar, env_var}; -use crate::{AllLanguageModelSettings, api_key::ApiKeyState, ui::InstructionListItem}; +use crate::{api_key::ApiKeyState, ui::InstructionListItem}; const PROVIDER_ID: LanguageModelProviderId = LanguageModelProviderId::new("openrouter"); const PROVIDER_NAME: LanguageModelProviderName = LanguageModelProviderName::new("OpenRouter"); @@ -96,7 +96,6 @@ pub struct State { http_client: Arc, available_models: Vec, fetch_models_task: Option>>, - settings: OpenRouterSettings, } impl State { @@ -171,14 +170,17 @@ impl State { impl OpenRouterLanguageModelProvider { pub fn new(http_client: Arc, cx: &mut App) -> Self { let state = cx.new(|cx| { - cx.observe_global::(|this: &mut State, cx| { - let current_settings = &AllLanguageModelSettings::get_global(cx).open_router; - let settings_changed = current_settings != &this.settings; - if settings_changed { - this.settings = current_settings.clone(); - this.authenticate(cx).detach(); + cx.observe_global::({ + let mut last_settings = OpenRouterLanguageModelProvider::settings(cx).clone(); + move |this: &mut State, cx| { + let current_settings = OpenRouterLanguageModelProvider::settings(cx); + let settings_changed = current_settings != &last_settings; + if settings_changed { + last_settings = current_settings.clone(); + this.authenticate(cx).detach(); + cx.notify(); + } } - cx.notify(); }) .detach(); State { @@ -186,7 +188,6 @@ impl OpenRouterLanguageModelProvider { http_client: http_client.clone(), available_models: Vec::new(), fetch_models_task: None, - settings: OpenRouterSettings::default(), } }); @@ -194,7 +195,7 @@ impl OpenRouterLanguageModelProvider { } fn settings(cx: &App) -> &OpenRouterSettings { - &AllLanguageModelSettings::get_global(cx).open_router + &crate::AllLanguageModelSettings::get_global(cx).open_router } fn api_url(cx: &App) -> SharedString { @@ -322,17 +323,11 @@ impl OpenRouterLanguageModel { >, > { let http_client = self.http_client.clone(); - let api_key_and_url = self.state.read_with(cx, |state, cx| { + let Ok((api_key, api_url)) = self.state.read_with(cx, |state, cx| { let api_url = OpenRouterLanguageModelProvider::api_url(cx); - let api_key = state.api_key_state.key(&api_url); - (api_key, api_url) - }); - let (api_key, api_url) = match api_key_and_url { - Ok(api_key_and_url) => api_key_and_url, - Err(err) => { - return futures::future::ready(Err(LanguageModelCompletionError::Other(err))) - .boxed(); - } + (state.api_key_state.key(&api_url), api_url) + }) else { + return future::ready(Err(anyhow!("App state dropped").into())).boxed(); }; async move { @@ -795,6 +790,10 @@ impl ConfigurationView { return; } + // url changes can cause the editor to be displayed again + self.api_key_editor + .update(cx, |editor, cx| editor.set_text("", window, cx)); + let state = self.state.clone(); cx.spawn_in(window, async move |_, cx| { state diff --git a/crates/language_models/src/provider/vercel.rs b/crates/language_models/src/provider/vercel.rs index ad28946e3bc78078d9d6510fafacbba34c1f98ca..abf63bd8e27b4ba08df8b7e2549fffc0578c138d 100644 --- a/crates/language_models/src/provider/vercel.rs +++ b/crates/language_models/src/provider/vercel.rs @@ -1,6 +1,6 @@ -use anyhow::Result; +use anyhow::{Result, anyhow}; use collections::BTreeMap; -use futures::{FutureExt, StreamExt, future::BoxFuture}; +use futures::{FutureExt, StreamExt, future, future::BoxFuture}; use gpui::{AnyView, App, AsyncApp, Context, Entity, SharedString, Task, Window}; use http_client::HttpClient; use language_model::{ @@ -219,16 +219,11 @@ impl VercelLanguageModel { { let http_client = self.http_client.clone(); - let api_key_and_url = self.state.read_with(cx, |state, cx| { + let Ok((api_key, api_url)) = self.state.read_with(cx, |state, cx| { let api_url = VercelLanguageModelProvider::api_url(cx); - let api_key = state.api_key_state.key(&api_url); - (api_key, api_url) - }); - let (api_key, api_url) = match api_key_and_url { - Ok(api_key_and_url) => api_key_and_url, - Err(err) => { - return futures::future::ready(Err(err)).boxed(); - } + (state.api_key_state.key(&api_url), api_url) + }) else { + return future::ready(Err(anyhow!("App state dropped"))).boxed(); }; let future = self.request_limiter.stream(async move { @@ -429,6 +424,10 @@ impl ConfigurationView { return; } + // url changes can cause the editor to be displayed again + self.api_key_editor + .update(cx, |editor, cx| editor.set_text("", window, cx)); + let state = self.state.clone(); cx.spawn_in(window, async move |_, cx| { state @@ -439,11 +438,8 @@ impl ConfigurationView { } fn reset_api_key(&mut self, window: &mut Window, cx: &mut Context) { - self.api_key_editor.update(cx, |input, cx| { - input.editor.update(cx, |editor, cx| { - editor.set_text("", window, cx); - }); - }); + self.api_key_editor + .update(cx, |input, cx| input.set_text("", window, cx)); let state = self.state.clone(); cx.spawn_in(window, async move |_, cx| { diff --git a/crates/language_models/src/provider/x_ai.rs b/crates/language_models/src/provider/x_ai.rs index d5c5848293da5170b609bfd1526c26429865184c..eba95d1c146045b3e4482ffe2db34c8a166530ef 100644 --- a/crates/language_models/src/provider/x_ai.rs +++ b/crates/language_models/src/provider/x_ai.rs @@ -1,6 +1,6 @@ -use anyhow::Result; +use anyhow::{Result, anyhow}; use collections::BTreeMap; -use futures::{FutureExt, StreamExt, future::BoxFuture}; +use futures::{FutureExt, StreamExt, future, future::BoxFuture}; use gpui::{AnyView, App, AsyncApp, Context, Entity, Task, Window}; use http_client::HttpClient; use language_model::{ @@ -219,16 +219,11 @@ impl XAiLanguageModel { { let http_client = self.http_client.clone(); - let api_key_and_url = self.state.read_with(cx, |state, cx| { + let Ok((api_key, api_url)) = self.state.read_with(cx, |state, cx| { let api_url = XAiLanguageModelProvider::api_url(cx); - let api_key = state.api_key_state.key(&api_url); - (api_key, api_url) - }); - let (api_key, api_url) = match api_key_and_url { - Ok(api_key_and_url) => api_key_and_url, - Err(err) => { - return futures::future::ready(Err(err)).boxed(); - } + (state.api_key_state.key(&api_url), api_url) + }) else { + return future::ready(Err(anyhow!("App state dropped"))).boxed(); }; let future = self.request_limiter.stream(async move { @@ -423,6 +418,10 @@ impl ConfigurationView { return; } + // url changes can cause the editor to be displayed again + self.api_key_editor + .update(cx, |editor, cx| editor.set_text("", window, cx)); + let state = self.state.clone(); cx.spawn_in(window, async move |_, cx| { state @@ -433,11 +432,8 @@ impl ConfigurationView { } fn reset_api_key(&mut self, window: &mut Window, cx: &mut Context) { - self.api_key_editor.update(cx, |input, cx| { - input.editor.update(cx, |editor, cx| { - editor.set_text("", window, cx); - }); - }); + self.api_key_editor + .update(cx, |input, cx| input.set_text("", window, cx)); let state = self.state.clone(); cx.spawn_in(window, async move |_, cx| { diff --git a/crates/ui_input/src/ui_input.rs b/crates/ui_input/src/ui_input.rs index 86a569b53200cc5ef3ed144841e76cecd94ef94e..45c0deba4adfe71ea99d83c1bd081af1fc272671 100644 --- a/crates/ui_input/src/ui_input.rs +++ b/crates/ui_input/src/ui_input.rs @@ -9,6 +9,7 @@ use component::{example_group, single_example}; use editor::{Editor, EditorElement, EditorStyle}; use gpui::{App, Entity, FocusHandle, Focusable, FontStyle, Hsla, TextStyle}; use settings::Settings; +use std::sync::Arc; use theme::ThemeSettings; use ui::prelude::*; @@ -101,6 +102,11 @@ impl SingleLineInput { pub fn text(&self, cx: &App) -> String { self.editor().read(cx).text(cx) } + + pub fn set_text(&self, text: impl Into>, window: &mut Window, cx: &mut App) { + self.editor() + .update(cx, |editor, cx| editor.set_text(text, window, cx)) + } } impl Render for SingleLineInput {