SSH Remoting: Fix yes/no/fingerprint prompt (#19526)
Some checks are pending
CI / Check Postgres and Protobuf migrations, mergability (push) Waiting to run
CI / Check formatting and spelling (push) Waiting to run
CI / (macOS) Run Clippy and tests (push) Waiting to run
CI / (Linux) Run Clippy and tests (push) Waiting to run
CI / (Linux) Build Remote Server (push) Waiting to run
CI / (Windows) Run Clippy and tests (push) Waiting to run
CI / Create a macOS bundle (push) Blocked by required conditions
CI / Create a Linux bundle (push) Blocked by required conditions
CI / Create arm64 Linux bundle (push) Blocked by required conditions
Deploy Docs / Deploy Docs (push) Waiting to run
Docs / Check formatting (push) Waiting to run

Release Notes:

- SSH Remoting: fix SSH fingerprint prompt

Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
Conrad Irwin 2024-10-21 15:28:22 -06:00 committed by GitHub
parent 1a4b253ee5
commit 9bae93cd39
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 111 additions and 58 deletions

2
Cargo.lock generated
View file

@ -8986,6 +8986,7 @@ dependencies = [
"itertools 0.13.0", "itertools 0.13.0",
"language", "language",
"log", "log",
"markdown",
"menu", "menu",
"ordered-float 2.10.1", "ordered-float 2.10.1",
"paths", "paths",
@ -9001,6 +9002,7 @@ dependencies = [
"smol", "smol",
"task", "task",
"terminal_view", "terminal_view",
"theme",
"ui", "ui",
"util", "util",
"workspace", "workspace",

View file

@ -76,9 +76,9 @@ use gpui::{
ClipboardItem, Context, DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent, ClipboardItem, Context, DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent,
FocusableView, FontId, FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext, FocusableView, FontId, FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext,
ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString, ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString,
Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle, UTF16Selection, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle,
UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle, View,
WeakFocusHandle, WeakView, WindowContext, ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle, WeakView, WindowContext,
}; };
use highlight_matching_bracket::refresh_matching_bracket_highlights; use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState}; use hover_popover::{hide_hover, HoverState};
@ -546,6 +546,7 @@ pub struct Editor {
ime_transaction: Option<TransactionId>, ime_transaction: Option<TransactionId>,
active_diagnostics: Option<ActiveDiagnosticGroup>, active_diagnostics: Option<ActiveDiagnosticGroup>,
soft_wrap_mode_override: Option<language_settings::SoftWrap>, soft_wrap_mode_override: Option<language_settings::SoftWrap>,
project: Option<Model<Project>>, project: Option<Model<Project>>,
semantics_provider: Option<Rc<dyn SemanticsProvider>>, semantics_provider: Option<Rc<dyn SemanticsProvider>>,
completion_provider: Option<Box<dyn CompletionProvider>>, completion_provider: Option<Box<dyn CompletionProvider>>,
@ -615,6 +616,7 @@ pub struct Editor {
pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>, pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
gutter_dimensions: GutterDimensions, gutter_dimensions: GutterDimensions,
style: Option<EditorStyle>, style: Option<EditorStyle>,
text_style_refinement: Option<TextStyleRefinement>,
next_editor_action_id: EditorActionId, next_editor_action_id: EditorActionId,
editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>, editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
use_autoclose: bool, use_autoclose: bool,
@ -2062,6 +2064,7 @@ impl Editor {
next_scroll_position: NextScrollCursorCenterTopBottom::default(), next_scroll_position: NextScrollCursorCenterTopBottom::default(),
addons: HashMap::default(), addons: HashMap::default(),
_scroll_cursor_center_top_bottom_task: Task::ready(()), _scroll_cursor_center_top_bottom_task: Task::ready(()),
text_style_refinement: None,
}; };
this.tasks_update_task = Some(this.refresh_runnables(cx)); this.tasks_update_task = Some(this.refresh_runnables(cx));
this._subscriptions.extend(project_subscriptions); this._subscriptions.extend(project_subscriptions);
@ -11180,7 +11183,12 @@ impl Editor {
cx.notify(); cx.notify();
} }
pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) { 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<Self>) {
let rem_size = cx.rem_size(); let rem_size = cx.rem_size();
self.display_map.update(cx, |map, cx| { self.display_map.update(cx, |map, cx| {
map.set_font( map.set_font(
@ -13676,7 +13684,7 @@ impl Render for Editor {
fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement { fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx); let settings = ThemeSettings::get_global(cx);
let text_style = match self.mode { let mut text_style = match self.mode {
EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle { EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
color: cx.theme().colors().editor_foreground, color: cx.theme().colors().editor_foreground,
font_family: settings.ui_font.family.clone(), font_family: settings.ui_font.family.clone(),
@ -13698,6 +13706,9 @@ impl Render for Editor {
..Default::default() ..Default::default()
}, },
}; };
if let Some(text_style_refinement) = &self.text_style_refinement {
text_style.refine(text_style_refinement)
}
let background = match self.mode { let background = match self.mode {
EditorMode::SingleLine { .. } => cx.theme().system().transparent, EditorMode::SingleLine { .. } => cx.theme().system().transparent,

View file

@ -119,6 +119,10 @@ impl Markdown {
this this
} }
pub fn source(&self) -> &str {
&self.source
}
pub fn append(&mut self, text: &str, cx: &ViewContext<Self>) { pub fn append(&mut self, text: &str, cx: &ViewContext<Self>) {
self.source.push_str(text); self.source.push_str(text);
self.parse(cx); self.parse(cx);
@ -137,10 +141,6 @@ impl Markdown {
self.parse(cx); self.parse(cx);
} }
pub fn source(&self) -> &str {
&self.source
}
pub fn parsed_markdown(&self) -> &ParsedMarkdown { pub fn parsed_markdown(&self) -> &ParsedMarkdown {
&self.parsed_markdown &self.parsed_markdown
} }

View file

@ -24,6 +24,8 @@ fuzzy.workspace = true
gpui.workspace = true gpui.workspace = true
itertools.workspace = true itertools.workspace = true
log.workspace = true log.workspace = true
language.workspace = true
markdown.workspace = true
menu.workspace = true menu.workspace = true
ordered-float.workspace = true ordered-float.workspace = true
picker.workspace = true picker.workspace = true
@ -37,6 +39,7 @@ settings.workspace = true
smol.workspace = true smol.workspace = true
task.workspace = true task.workspace = true
terminal_view.workspace = true terminal_view.workspace = true
theme.workspace = true
ui.workspace = true ui.workspace = true
util.workspace = true util.workspace = true
workspace.workspace = true workspace.workspace = true

View file

@ -7,15 +7,18 @@ use futures::channel::oneshot;
use gpui::{ use gpui::{
percentage, px, Animation, AnimationExt, AnyWindowHandle, AsyncAppContext, DismissEvent, percentage, px, Animation, AnimationExt, AnyWindowHandle, AsyncAppContext, DismissEvent,
EventEmitter, FocusableView, ParentElement as _, Render, SemanticVersion, SharedString, Task, EventEmitter, FocusableView, ParentElement as _, Render, SemanticVersion, SharedString, Task,
Transformation, View, TextStyleRefinement, Transformation, View,
}; };
use gpui::{AppContext, Model}; use gpui::{AppContext, Model};
use language::CursorShape;
use markdown::{Markdown, MarkdownStyle};
use release_channel::{AppVersion, ReleaseChannel}; use release_channel::{AppVersion, ReleaseChannel};
use remote::{SshConnectionOptions, SshPlatform, SshRemoteClient}; use remote::{SshConnectionOptions, SshPlatform, SshRemoteClient};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources}; use settings::{Settings, SettingsSources};
use theme::ThemeSettings;
use ui::{ use ui::{
div, h_flex, prelude::*, v_flex, ActiveTheme, Color, Icon, IconName, IconSize, div, h_flex, prelude::*, v_flex, ActiveTheme, Color, Icon, IconName, IconSize,
InteractiveElement, IntoElement, Label, LabelCommon, Styled, ViewContext, VisualContext, InteractiveElement, IntoElement, Label, LabelCommon, Styled, ViewContext, VisualContext,
@ -102,7 +105,7 @@ pub struct SshPrompt {
connection_string: SharedString, connection_string: SharedString,
status_message: Option<SharedString>, status_message: Option<SharedString>,
error_message: Option<SharedString>, error_message: Option<SharedString>,
prompt: Option<(SharedString, oneshot::Sender<Result<String>>)>, prompt: Option<(View<Markdown>, oneshot::Sender<Result<String>>)>,
editor: View<Editor>, editor: View<Editor>,
} }
@ -132,14 +135,34 @@ impl SshPrompt {
tx: oneshot::Sender<Result<String>>, tx: oneshot::Sender<Result<String>>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
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| { self.editor.update(cx, |editor, cx| {
if prompt.contains("yes/no") { if prompt.contains("yes/no") {
editor.set_masked(false, cx); editor.set_masked(false, cx);
} else { } else {
editor.set_masked(true, cx); 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(); self.status_message.take();
cx.focus_view(&self.editor); cx.focus_view(&self.editor);
cx.notify(); cx.notify();
@ -157,6 +180,7 @@ impl SshPrompt {
pub fn confirm(&mut self, cx: &mut ViewContext<Self>) { pub fn confirm(&mut self, cx: &mut ViewContext<Self>) {
if let Some((_, tx)) = self.prompt.take() { if let Some((_, tx)) = self.prompt.take() {
self.status_message = Some("Connecting".into());
self.editor.update(cx, |editor, cx| { self.editor.update(cx, |editor, cx| {
tx.send(Ok(editor.text(cx))).ok(); tx.send(Ok(editor.text(cx))).ok();
editor.clear(cx); editor.clear(cx);
@ -172,60 +196,73 @@ impl Render for SshPrompt {
v_flex() v_flex()
.key_context("PasswordPrompt") .key_context("PasswordPrompt")
.size_full() .size_full()
.child( .when(
h_flex() self.error_message.is_some() || self.status_message.is_some(),
.p_2() |el| {
.flex() el.child(
.child(if self.error_message.is_some() { h_flex()
Icon::new(IconName::XCircle) .p_2()
.size(IconSize::Medium) .flex()
.color(Color::Error) .child(if self.error_message.is_some() {
.into_any_element() Icon::new(IconName::XCircle)
} else { .size(IconSize::Medium)
Icon::new(IconName::ArrowCircle) .color(Color::Error)
.size(IconSize::Medium) .into_any_element()
.with_animation( } else {
"arrow-circle", Icon::new(IconName::ArrowCircle)
Animation::new(Duration::from_secs(2)).repeat(), .size(IconSize::Medium)
|icon, delta| { .with_animation(
icon.transform(Transformation::rotate(percentage(delta))) "arrow-circle",
}, Animation::new(Duration::from_secs(2)).repeat(),
) |icon, delta| {
.into_any_element() icon.transform(Transformation::rotate(percentage(
}) delta,
.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),
) )
}, .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( el.child(
h_flex() div()
.size_full()
.overflow_hidden()
.p_4() .p_4()
.border_t_1() .border_t_1()
.border_color(theme.colors().border_variant) .border_color(theme.colors().border_variant)
.font_buffer(cx) .font_buffer(cx)
.child(Label::new(prompt.0.clone())) .text_buffer(cx)
.child(prompt.0.clone())
.child(self.editor.clone()), .child(self.editor.clone()),
) )
})) })
} }
} }

View file

@ -1202,7 +1202,7 @@ impl SshRemoteConnection {
use smol::{fs::unix::PermissionsExt as _, net::unix::UnixListener}; use smol::{fs::unix::PermissionsExt as _, net::unix::UnixListener};
use util::ResultExt as _; use util::ResultExt as _;
delegate.set_status(Some("connecting"), cx); delegate.set_status(Some("Connecting"), cx);
let url = connection_options.ssh_url(); let url = connection_options.ssh_url();
let temp_dir = tempfile::Builder::new() let temp_dir = tempfile::Builder::new()

View file

@ -79,7 +79,7 @@ pub trait StyledTypography: Styled + Sized {
/// ///
/// This should only be used for text that is displayed in a buffer, /// 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. /// 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); let settings = ThemeSettings::get_global(cx);
self.text_size(settings.buffer_font_size(cx)) self.text_size(settings.buffer_font_size(cx))
} }