From 9bae93cd39f19173dfd92f6c030e506b802c3ed4 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 21 Oct 2024 15:28:22 -0600 Subject: [PATCH] SSH Remoting: Fix yes/no/fingerprint prompt (#19526) Release Notes: - SSH Remoting: fix SSH fingerprint prompt Co-authored-by: Mikayla --- Cargo.lock | 2 + crates/editor/src/editor.rs | 21 ++- crates/markdown/src/markdown.rs | 8 +- crates/recent_projects/Cargo.toml | 3 + crates/recent_projects/src/ssh_connections.rs | 131 +++++++++++------- crates/remote/src/ssh_session.rs | 2 +- crates/ui/src/styles/typography.rs | 2 +- 7 files changed, 111 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4436594b8b..fbd6b84f77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8986,6 +8986,7 @@ dependencies = [ "itertools 0.13.0", "language", "log", + "markdown", "menu", "ordered-float 2.10.1", "paths", @@ -9001,6 +9002,7 @@ dependencies = [ "smol", "task", "terminal_view", + "theme", "ui", "util", "workspace", diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 211a6568b7..e1280850a2 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -76,9 +76,9 @@ use gpui::{ ClipboardItem, Context, DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent, FocusableView, FontId, FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext, ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString, - Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle, UTF16Selection, - UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, - WeakFocusHandle, WeakView, WindowContext, + Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle, + TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle, View, + ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle, WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -546,6 +546,7 @@ pub struct Editor { ime_transaction: Option, active_diagnostics: Option, soft_wrap_mode_override: Option, + project: Option>, semantics_provider: Option>, completion_provider: Option>, @@ -615,6 +616,7 @@ pub struct Editor { pixel_position_of_newest_cursor: Option>, gutter_dimensions: GutterDimensions, style: Option, + text_style_refinement: Option, next_editor_action_id: EditorActionId, editor_actions: Rc)>>>>, use_autoclose: bool, @@ -2062,6 +2064,7 @@ impl Editor { next_scroll_position: NextScrollCursorCenterTopBottom::default(), addons: HashMap::default(), _scroll_cursor_center_top_bottom_task: Task::ready(()), + text_style_refinement: None, }; this.tasks_update_task = Some(this.refresh_runnables(cx)); this._subscriptions.extend(project_subscriptions); @@ -11180,7 +11183,12 @@ impl Editor { cx.notify(); } - pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext) { + pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) { + self.text_style_refinement = Some(style); + } + + /// called by the Element so we know what style we were most recently rendered with. + pub(crate) fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext) { let rem_size = cx.rem_size(); self.display_map.update(cx, |map, cx| { map.set_font( @@ -13676,7 +13684,7 @@ impl Render for Editor { fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement { let settings = ThemeSettings::get_global(cx); - let text_style = match self.mode { + let mut text_style = match self.mode { EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle { color: cx.theme().colors().editor_foreground, font_family: settings.ui_font.family.clone(), @@ -13698,6 +13706,9 @@ impl Render for Editor { ..Default::default() }, }; + if let Some(text_style_refinement) = &self.text_style_refinement { + text_style.refine(text_style_refinement) + } let background = match self.mode { EditorMode::SingleLine { .. } => cx.theme().system().transparent, diff --git a/crates/markdown/src/markdown.rs b/crates/markdown/src/markdown.rs index 47a0e03c2a..203d2df6a4 100644 --- a/crates/markdown/src/markdown.rs +++ b/crates/markdown/src/markdown.rs @@ -119,6 +119,10 @@ impl Markdown { this } + pub fn source(&self) -> &str { + &self.source + } + pub fn append(&mut self, text: &str, cx: &ViewContext) { self.source.push_str(text); self.parse(cx); @@ -137,10 +141,6 @@ impl Markdown { self.parse(cx); } - pub fn source(&self) -> &str { - &self.source - } - pub fn parsed_markdown(&self) -> &ParsedMarkdown { &self.parsed_markdown } diff --git a/crates/recent_projects/Cargo.toml b/crates/recent_projects/Cargo.toml index c954924711..f69c6c1c21 100644 --- a/crates/recent_projects/Cargo.toml +++ b/crates/recent_projects/Cargo.toml @@ -24,6 +24,8 @@ fuzzy.workspace = true gpui.workspace = true itertools.workspace = true log.workspace = true +language.workspace = true +markdown.workspace = true menu.workspace = true ordered-float.workspace = true picker.workspace = true @@ -37,6 +39,7 @@ settings.workspace = true smol.workspace = true task.workspace = true terminal_view.workspace = true +theme.workspace = true ui.workspace = true util.workspace = true workspace.workspace = true diff --git a/crates/recent_projects/src/ssh_connections.rs b/crates/recent_projects/src/ssh_connections.rs index 814126e9f9..29f74dbb0d 100644 --- a/crates/recent_projects/src/ssh_connections.rs +++ b/crates/recent_projects/src/ssh_connections.rs @@ -7,15 +7,18 @@ use futures::channel::oneshot; use gpui::{ percentage, px, Animation, AnimationExt, AnyWindowHandle, AsyncAppContext, DismissEvent, EventEmitter, FocusableView, ParentElement as _, Render, SemanticVersion, SharedString, Task, - Transformation, View, + TextStyleRefinement, Transformation, View, }; use gpui::{AppContext, Model}; +use language::CursorShape; +use markdown::{Markdown, MarkdownStyle}; use release_channel::{AppVersion, ReleaseChannel}; use remote::{SshConnectionOptions, SshPlatform, SshRemoteClient}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; +use theme::ThemeSettings; use ui::{ div, h_flex, prelude::*, v_flex, ActiveTheme, Color, Icon, IconName, IconSize, InteractiveElement, IntoElement, Label, LabelCommon, Styled, ViewContext, VisualContext, @@ -102,7 +105,7 @@ pub struct SshPrompt { connection_string: SharedString, status_message: Option, error_message: Option, - prompt: Option<(SharedString, oneshot::Sender>)>, + prompt: Option<(View, oneshot::Sender>)>, editor: View, } @@ -132,14 +135,34 @@ impl SshPrompt { tx: oneshot::Sender>, cx: &mut ViewContext, ) { + let theme = ThemeSettings::get_global(cx); + + let mut text_style = cx.text_style(); + let refinement = TextStyleRefinement { + font_family: Some(theme.buffer_font.family.clone()), + font_size: Some(theme.buffer_font_size.into()), + color: Some(cx.theme().colors().editor_foreground), + background_color: Some(gpui::transparent_black()), + ..Default::default() + }; + + text_style.refine(&refinement); self.editor.update(cx, |editor, cx| { if prompt.contains("yes/no") { editor.set_masked(false, cx); } else { editor.set_masked(true, cx); } + editor.set_text_style_refinement(refinement); + editor.set_cursor_shape(CursorShape::Block, cx); }); - self.prompt = Some((prompt.into(), tx)); + let markdown_style = MarkdownStyle { + base_text_style: text_style, + selection_background_color: cx.theme().players().local().selection, + ..Default::default() + }; + let markdown = cx.new_view(|cx| Markdown::new_text(prompt, markdown_style, None, cx, None)); + self.prompt = Some((markdown, tx)); self.status_message.take(); cx.focus_view(&self.editor); cx.notify(); @@ -157,6 +180,7 @@ impl SshPrompt { pub fn confirm(&mut self, cx: &mut ViewContext) { if let Some((_, tx)) = self.prompt.take() { + self.status_message = Some("Connecting".into()); self.editor.update(cx, |editor, cx| { tx.send(Ok(editor.text(cx))).ok(); editor.clear(cx); @@ -172,60 +196,73 @@ impl Render for SshPrompt { v_flex() .key_context("PasswordPrompt") .size_full() - .child( - h_flex() - .p_2() - .flex() - .child(if self.error_message.is_some() { - Icon::new(IconName::XCircle) - .size(IconSize::Medium) - .color(Color::Error) - .into_any_element() - } else { - Icon::new(IconName::ArrowCircle) - .size(IconSize::Medium) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| { - icon.transform(Transformation::rotate(percentage(delta))) - }, - ) - .into_any_element() - }) - .child( - div() - .ml_1() - .text_ellipsis() - .overflow_x_hidden() - .when_some(self.error_message.as_ref(), |el, error| { - el.child(Label::new(format!("{}", error)).size(LabelSize::Small)) - }) - .when( - self.error_message.is_none() && self.status_message.is_some(), - |el| { - el.child( - Label::new(format!( - "{}…", - self.status_message.clone().unwrap() - )) - .size(LabelSize::Small), + .when( + self.error_message.is_some() || self.status_message.is_some(), + |el| { + el.child( + h_flex() + .p_2() + .flex() + .child(if self.error_message.is_some() { + Icon::new(IconName::XCircle) + .size(IconSize::Medium) + .color(Color::Error) + .into_any_element() + } else { + Icon::new(IconName::ArrowCircle) + .size(IconSize::Medium) + .with_animation( + "arrow-circle", + Animation::new(Duration::from_secs(2)).repeat(), + |icon, delta| { + icon.transform(Transformation::rotate(percentage( + delta, + ))) + }, ) - }, + .into_any_element() + }) + .child( + div() + .ml_1() + .text_ellipsis() + .overflow_x_hidden() + .when_some(self.error_message.as_ref(), |el, error| { + el.child( + Label::new(format!("{}", error)).size(LabelSize::Small), + ) + }) + .when( + self.error_message.is_none() + && self.status_message.is_some(), + |el| { + el.child( + Label::new(format!( + "{}…", + self.status_message.clone().unwrap() + )) + .size(LabelSize::Small), + ) + }, + ), ), - ), + ) + }, ) - .child(div().when_some(self.prompt.as_ref(), |el, prompt| { + .when_some(self.prompt.as_ref(), |el, prompt| { el.child( - h_flex() + div() + .size_full() + .overflow_hidden() .p_4() .border_t_1() .border_color(theme.colors().border_variant) .font_buffer(cx) - .child(Label::new(prompt.0.clone())) + .text_buffer(cx) + .child(prompt.0.clone()) .child(self.editor.clone()), ) - })) + }) } } diff --git a/crates/remote/src/ssh_session.rs b/crates/remote/src/ssh_session.rs index 5926a0b896..979dbacc62 100644 --- a/crates/remote/src/ssh_session.rs +++ b/crates/remote/src/ssh_session.rs @@ -1202,7 +1202,7 @@ impl SshRemoteConnection { use smol::{fs::unix::PermissionsExt as _, net::unix::UnixListener}; use util::ResultExt as _; - delegate.set_status(Some("connecting"), cx); + delegate.set_status(Some("Connecting"), cx); let url = connection_options.ssh_url(); let temp_dir = tempfile::Builder::new() diff --git a/crates/ui/src/styles/typography.rs b/crates/ui/src/styles/typography.rs index ef9c946ed5..314d5339f0 100644 --- a/crates/ui/src/styles/typography.rs +++ b/crates/ui/src/styles/typography.rs @@ -79,7 +79,7 @@ pub trait StyledTypography: Styled + Sized { /// /// This should only be used for text that is displayed in a buffer, /// or other places that text needs to match the user's buffer font size. - fn text_buffer(self, cx: &mut WindowContext) -> Self { + fn text_buffer(self, cx: &WindowContext) -> Self { let settings = ThemeSettings::get_global(cx); self.text_size(settings.buffer_font_size(cx)) }