mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-28 09:54:33 +00:00
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
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:
parent
1a4b253ee5
commit
9bae93cd39
7 changed files with 111 additions and 58 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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<TransactionId>,
|
||||
active_diagnostics: Option<ActiveDiagnosticGroup>,
|
||||
soft_wrap_mode_override: Option<language_settings::SoftWrap>,
|
||||
|
||||
project: Option<Model<Project>>,
|
||||
semantics_provider: Option<Rc<dyn SemanticsProvider>>,
|
||||
completion_provider: Option<Box<dyn CompletionProvider>>,
|
||||
|
@ -615,6 +616,7 @@ pub struct Editor {
|
|||
pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
|
||||
gutter_dimensions: GutterDimensions,
|
||||
style: Option<EditorStyle>,
|
||||
text_style_refinement: Option<TextStyleRefinement>,
|
||||
next_editor_action_id: EditorActionId,
|
||||
editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
|
||||
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<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();
|
||||
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,
|
||||
|
|
|
@ -119,6 +119,10 @@ impl Markdown {
|
|||
this
|
||||
}
|
||||
|
||||
pub fn source(&self) -> &str {
|
||||
&self.source
|
||||
}
|
||||
|
||||
pub fn append(&mut self, text: &str, cx: &ViewContext<Self>) {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<SharedString>,
|
||||
error_message: Option<SharedString>,
|
||||
prompt: Option<(SharedString, oneshot::Sender<Result<String>>)>,
|
||||
prompt: Option<(View<Markdown>, oneshot::Sender<Result<String>>)>,
|
||||
editor: View<Editor>,
|
||||
}
|
||||
|
||||
|
@ -132,14 +135,34 @@ impl SshPrompt {
|
|||
tx: oneshot::Sender<Result<String>>,
|
||||
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| {
|
||||
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<Self>) {
|
||||
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()),
|
||||
)
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue