markdown: Don't retain MarkdownStyle in favor of using MarkdownElement directly (#28255)

This PR removes the retained `MarkdownStyle` on the `Markdown` entity in
favor of using the `MarkdownElement` directly and passing the
`MarkdownStyle` to it.

This makes it so switching themes will be reflected live in the code
block styles.

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
This commit is contained in:
Marshall Bowers 2025-04-07 15:03:24 -04:00 committed by GitHub
parent aa026156f2
commit b6ee367ee0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 370 additions and 374 deletions

View file

@ -22,7 +22,7 @@ use gpui::{
}; };
use language::{Buffer, LanguageRegistry}; use language::{Buffer, LanguageRegistry};
use language_model::{ConfiguredModel, LanguageModelRegistry, LanguageModelToolUseId, Role}; use language_model::{ConfiguredModel, LanguageModelRegistry, LanguageModelToolUseId, Role};
use markdown::{Markdown, MarkdownStyle}; use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use project::ProjectItem as _; use project::ProjectItem as _;
use settings::{Settings as _, update_settings_file}; use settings::{Settings as _, update_settings_file};
use std::rc::Rc; use std::rc::Rc;
@ -77,7 +77,6 @@ impl RenderedMessage {
segments: &[MessageSegment], segments: &[MessageSegment],
language_registry: Arc<LanguageRegistry>, language_registry: Arc<LanguageRegistry>,
workspace: WeakEntity<Workspace>, workspace: WeakEntity<Workspace>,
window: &Window,
cx: &mut App, cx: &mut App,
) -> Self { ) -> Self {
let mut this = Self { let mut this = Self {
@ -85,18 +84,12 @@ impl RenderedMessage {
segments: Vec::with_capacity(segments.len()), segments: Vec::with_capacity(segments.len()),
}; };
for segment in segments { for segment in segments {
this.push_segment(segment, workspace.clone(), window, cx); this.push_segment(segment, workspace.clone(), cx);
} }
this this
} }
fn append_thinking( fn append_thinking(&mut self, text: &String, workspace: WeakEntity<Workspace>, cx: &mut App) {
&mut self,
text: &String,
workspace: WeakEntity<Workspace>,
window: &Window,
cx: &mut App,
) {
if let Some(RenderedMessageSegment::Thinking { if let Some(RenderedMessageSegment::Thinking {
content, content,
scroll_handle, scroll_handle,
@ -112,7 +105,6 @@ impl RenderedMessage {
text.into(), text.into(),
self.language_registry.clone(), self.language_registry.clone(),
workspace, workspace,
window,
cx, cx,
), ),
scroll_handle: ScrollHandle::default(), scroll_handle: ScrollHandle::default(),
@ -120,13 +112,7 @@ impl RenderedMessage {
} }
} }
fn append_text( fn append_text(&mut self, text: &String, workspace: WeakEntity<Workspace>, cx: &mut App) {
&mut self,
text: &String,
workspace: WeakEntity<Workspace>,
window: &Window,
cx: &mut App,
) {
if let Some(RenderedMessageSegment::Text(markdown)) = self.segments.last_mut() { if let Some(RenderedMessageSegment::Text(markdown)) = self.segments.last_mut() {
markdown.update(cx, |markdown, cx| markdown.append(text, cx)); markdown.update(cx, |markdown, cx| markdown.append(text, cx));
} else { } else {
@ -135,7 +121,6 @@ impl RenderedMessage {
SharedString::from(text), SharedString::from(text),
self.language_registry.clone(), self.language_registry.clone(),
workspace, workspace,
window,
cx, cx,
))); )));
} }
@ -145,7 +130,6 @@ impl RenderedMessage {
&mut self, &mut self,
segment: &MessageSegment, segment: &MessageSegment,
workspace: WeakEntity<Workspace>, workspace: WeakEntity<Workspace>,
window: &Window,
cx: &mut App, cx: &mut App,
) { ) {
let rendered_segment = match segment { let rendered_segment = match segment {
@ -154,7 +138,6 @@ impl RenderedMessage {
text.into(), text.into(),
self.language_registry.clone(), self.language_registry.clone(),
workspace, workspace,
window,
cx, cx,
), ),
scroll_handle: ScrollHandle::default(), scroll_handle: ScrollHandle::default(),
@ -163,7 +146,6 @@ impl RenderedMessage {
text.into(), text.into(),
self.language_registry.clone(), self.language_registry.clone(),
workspace, workspace,
window,
cx, cx,
)), )),
}; };
@ -183,9 +165,16 @@ fn render_markdown(
text: SharedString, text: SharedString,
language_registry: Arc<LanguageRegistry>, language_registry: Arc<LanguageRegistry>,
workspace: WeakEntity<Workspace>, workspace: WeakEntity<Workspace>,
window: &Window,
cx: &mut App, cx: &mut App,
) -> Entity<Markdown> { ) -> Entity<Markdown> {
cx.new(|cx| {
Markdown::new(text, Some(language_registry), None, cx).open_url(move |text, window, cx| {
open_markdown_link(text, workspace.clone(), window, cx);
})
})
}
fn default_markdown_style(window: &Window, cx: &App) -> MarkdownStyle {
let theme_settings = ThemeSettings::get_global(cx); let theme_settings = ThemeSettings::get_global(cx);
let colors = cx.theme().colors(); let colors = cx.theme().colors();
let ui_font_size = TextSize::Default.rems(cx); let ui_font_size = TextSize::Default.rems(cx);
@ -201,7 +190,7 @@ fn render_markdown(
..Default::default() ..Default::default()
}); });
let markdown_style = MarkdownStyle { MarkdownStyle {
base_text_style: text_style, base_text_style: text_style,
syntax: cx.theme().syntax().clone(), syntax: cx.theme().syntax().clone(),
selection_background_color: cx.theme().players().local().selection, selection_background_color: cx.theme().players().local().selection,
@ -266,24 +255,23 @@ fn render_markdown(
} }
})), })),
..Default::default() ..Default::default()
}; }
cx.new(|cx| {
Markdown::new(text, markdown_style, Some(language_registry), None, cx).open_url(
move |text, window, cx| {
open_markdown_link(text, workspace.clone(), window, cx);
},
)
})
} }
fn render_tool_use_markdown( fn render_tool_use_markdown(
text: SharedString, text: SharedString,
language_registry: Arc<LanguageRegistry>, language_registry: Arc<LanguageRegistry>,
workspace: WeakEntity<Workspace>, workspace: WeakEntity<Workspace>,
window: &Window,
cx: &mut App, cx: &mut App,
) -> Entity<Markdown> { ) -> Entity<Markdown> {
cx.new(|cx| {
Markdown::new(text, Some(language_registry), None, cx).open_url(move |text, window, cx| {
open_markdown_link(text, workspace.clone(), window, cx);
})
})
}
fn tool_use_markdown_style(window: &Window, cx: &mut App) -> MarkdownStyle {
let theme_settings = ThemeSettings::get_global(cx); let theme_settings = ThemeSettings::get_global(cx);
let colors = cx.theme().colors(); let colors = cx.theme().colors();
let ui_font_size = TextSize::Default.rems(cx); let ui_font_size = TextSize::Default.rems(cx);
@ -299,7 +287,7 @@ fn render_tool_use_markdown(
..Default::default() ..Default::default()
}); });
let markdown_style = MarkdownStyle { MarkdownStyle {
base_text_style: text_style, base_text_style: text_style,
syntax: cx.theme().syntax().clone(), syntax: cx.theme().syntax().clone(),
selection_background_color: cx.theme().players().local().selection, selection_background_color: cx.theme().players().local().selection,
@ -334,15 +322,7 @@ fn render_tool_use_markdown(
..Default::default() ..Default::default()
}, },
..Default::default() ..Default::default()
}; }
cx.new(|cx| {
Markdown::new(text, markdown_style, Some(language_registry), None, cx).open_url(
move |text, window, cx| {
open_markdown_link(text, workspace.clone(), window, cx);
},
)
})
} }
fn open_markdown_link( fn open_markdown_link(
@ -473,7 +453,6 @@ impl ActiveThread {
tool_use.ui_text.clone(), tool_use.ui_text.clone(),
&tool_use.input, &tool_use.input,
tool_use.status.text(), tool_use.status.text(),
window,
cx, cx,
); );
} }
@ -516,7 +495,7 @@ impl ActiveThread {
&mut self, &mut self,
id: &MessageId, id: &MessageId,
segments: &[MessageSegment], segments: &[MessageSegment],
window: &mut Window, _window: &mut Window,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
let old_len = self.messages.len(); let old_len = self.messages.len();
@ -527,7 +506,6 @@ impl ActiveThread {
segments, segments,
self.language_registry.clone(), self.language_registry.clone(),
self.workspace.clone(), self.workspace.clone(),
window,
cx, cx,
); );
self.rendered_messages_by_id.insert(*id, rendered_message); self.rendered_messages_by_id.insert(*id, rendered_message);
@ -537,7 +515,7 @@ impl ActiveThread {
&mut self, &mut self,
id: &MessageId, id: &MessageId,
segments: &[MessageSegment], segments: &[MessageSegment],
window: &mut Window, _window: &mut Window,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
let Some(index) = self.messages.iter().position(|message_id| message_id == id) else { let Some(index) = self.messages.iter().position(|message_id| message_id == id) else {
@ -548,7 +526,6 @@ impl ActiveThread {
segments, segments,
self.language_registry.clone(), self.language_registry.clone(),
self.workspace.clone(), self.workspace.clone(),
window,
cx, cx,
); );
self.rendered_messages_by_id.insert(*id, rendered_message); self.rendered_messages_by_id.insert(*id, rendered_message);
@ -569,7 +546,6 @@ impl ActiveThread {
tool_label: impl Into<SharedString>, tool_label: impl Into<SharedString>,
tool_input: &serde_json::Value, tool_input: &serde_json::Value,
tool_output: SharedString, tool_output: SharedString,
window: &mut Window,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
let rendered = RenderedToolUse { let rendered = RenderedToolUse {
@ -577,7 +553,6 @@ impl ActiveThread {
tool_label.into(), tool_label.into(),
self.language_registry.clone(), self.language_registry.clone(),
self.workspace.clone(), self.workspace.clone(),
window,
cx, cx,
), ),
input: render_tool_use_markdown( input: render_tool_use_markdown(
@ -588,14 +563,12 @@ impl ActiveThread {
.into(), .into(),
self.language_registry.clone(), self.language_registry.clone(),
self.workspace.clone(), self.workspace.clone(),
window,
cx, cx,
), ),
output: render_tool_use_markdown( output: render_tool_use_markdown(
tool_output, tool_output,
self.language_registry.clone(), self.language_registry.clone(),
self.workspace.clone(), self.workspace.clone(),
window,
cx, cx,
), ),
}; };
@ -640,12 +613,12 @@ impl ActiveThread {
} }
ThreadEvent::StreamedAssistantText(message_id, text) => { ThreadEvent::StreamedAssistantText(message_id, text) => {
if let Some(rendered_message) = self.rendered_messages_by_id.get_mut(&message_id) { if let Some(rendered_message) = self.rendered_messages_by_id.get_mut(&message_id) {
rendered_message.append_text(text, self.workspace.clone(), window, cx); rendered_message.append_text(text, self.workspace.clone(), cx);
} }
} }
ThreadEvent::StreamedAssistantThinking(message_id, text) => { ThreadEvent::StreamedAssistantThinking(message_id, text) => {
if let Some(rendered_message) = self.rendered_messages_by_id.get_mut(&message_id) { if let Some(rendered_message) = self.rendered_messages_by_id.get_mut(&message_id) {
rendered_message.append_thinking(text, self.workspace.clone(), window, cx); rendered_message.append_thinking(text, self.workspace.clone(), cx);
} }
} }
ThreadEvent::MessageAdded(message_id) => { ThreadEvent::MessageAdded(message_id) => {
@ -690,7 +663,6 @@ impl ActiveThread {
tool_use.ui_text.clone(), tool_use.ui_text.clone(),
&tool_use.input, &tool_use.input,
"".into(), "".into(),
window,
cx, cx,
); );
} }
@ -711,7 +683,6 @@ impl ActiveThread {
.tool_result(&tool_use.id) .tool_result(&tool_use.id)
.map(|result| result.content.clone().into()) .map(|result| result.content.clone().into())
.unwrap_or("".into()), .unwrap_or("".into()),
window,
cx, cx,
); );
} }
@ -1204,6 +1175,7 @@ impl ActiveThread {
message_id, message_id,
rendered_message, rendered_message,
has_tool_uses, has_tool_uses,
window,
cx, cx,
)) ))
.into_any() .into_any()
@ -1370,7 +1342,7 @@ impl ActiveThread {
div().children( div().children(
tool_uses tool_uses
.into_iter() .into_iter()
.map(|tool_use| self.render_tool_use(tool_use, cx)), .map(|tool_use| self.render_tool_use(tool_use, window, cx)),
), ),
) )
}), }),
@ -1540,6 +1512,7 @@ impl ActiveThread {
message_id: MessageId, message_id: MessageId,
rendered_message: &RenderedMessage, rendered_message: &RenderedMessage,
has_tool_uses: bool, has_tool_uses: bool,
window: &Window,
cx: &Context<Self>, cx: &Context<Self>,
) -> impl IntoElement { ) -> impl IntoElement {
let is_last_message = self.messages.last() == Some(&message_id); let is_last_message = self.messages.last() == Some(&message_id);
@ -1572,12 +1545,16 @@ impl ActiveThread {
content.clone(), content.clone(),
&scroll_handle, &scroll_handle,
Some(index) == pending_thinking_segment_index, Some(index) == pending_thinking_segment_index,
window,
cx, cx,
) )
.into_any_element(), .into_any_element(),
RenderedMessageSegment::Text(markdown) => { RenderedMessageSegment::Text(markdown) => div()
div().child(markdown.clone()).into_any_element() .child(MarkdownElement::new(
} markdown.clone(),
default_markdown_style(window, cx),
))
.into_any_element(),
}, },
), ),
) )
@ -1601,6 +1578,7 @@ impl ActiveThread {
markdown: Entity<Markdown>, markdown: Entity<Markdown>,
scroll_handle: &ScrollHandle, scroll_handle: &ScrollHandle,
pending: bool, pending: bool,
window: &Window,
cx: &Context<Self>, cx: &Context<Self>,
) -> impl IntoElement { ) -> impl IntoElement {
let is_open = self let is_open = self
@ -1734,7 +1712,10 @@ impl ActiveThread {
.h_20() .h_20()
.track_scroll(scroll_handle) .track_scroll(scroll_handle)
.text_ui_sm(cx) .text_ui_sm(cx)
.child(markdown.clone()) .child(MarkdownElement::new(
markdown.clone(),
default_markdown_style(window, cx),
))
.overflow_hidden(), .overflow_hidden(),
) )
.child(gradient_overlay), .child(gradient_overlay),
@ -1749,7 +1730,10 @@ impl ActiveThread {
.rounded_b_lg() .rounded_b_lg()
.bg(editor_bg) .bg(editor_bg)
.text_ui_sm(cx) .text_ui_sm(cx)
.child(markdown.clone()), .child(MarkdownElement::new(
markdown.clone(),
default_markdown_style(window, cx),
)),
) )
}), }),
) )
@ -1758,6 +1742,7 @@ impl ActiveThread {
fn render_tool_use( fn render_tool_use(
&self, &self,
tool_use: ToolUse, tool_use: ToolUse,
window: &mut Window,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> impl IntoElement + use<> { ) -> impl IntoElement + use<> {
let is_open = self let is_open = self
@ -1804,103 +1789,109 @@ impl ActiveThread {
let rendered_tool_use = self.rendered_tool_uses.get(&tool_use.id).cloned(); let rendered_tool_use = self.rendered_tool_uses.get(&tool_use.id).cloned();
let results_content_container = || v_flex().p_2().gap_0p5(); let results_content_container = || v_flex().p_2().gap_0p5();
let results_content = v_flex() let results_content =
.gap_1() v_flex()
.child( .gap_1()
results_content_container() .child(
.child(
Label::new("Input")
.size(LabelSize::XSmall)
.color(Color::Muted)
.buffer_font(cx),
)
.child(
div().w_full().text_ui_sm(cx).children(
rendered_tool_use
.as_ref()
.map(|rendered| rendered.input.clone()),
),
),
)
.map(|container| match tool_use.status {
ToolUseStatus::Finished(_) => container.child(
results_content_container() results_content_container()
.border_t_1()
.border_color(self.tool_card_border_color(cx))
.child( .child(
Label::new("Result") Label::new("Input")
.size(LabelSize::XSmall) .size(LabelSize::XSmall)
.color(Color::Muted) .color(Color::Muted)
.buffer_font(cx), .buffer_font(cx),
) )
.child( .child(div().w_full().text_ui_sm(cx).children(
div().w_full().text_ui_sm(cx).children( rendered_tool_use.as_ref().map(|rendered| {
rendered_tool_use MarkdownElement::new(
.as_ref() rendered.input.clone(),
.map(|rendered| rendered.output.clone()), tool_use_markdown_style(window, cx),
), )
), }),
), )),
ToolUseStatus::Running => container.child( )
results_content_container().child( .map(|container| match tool_use.status {
h_flex() ToolUseStatus::Finished(_) => container.child(
.gap_1() results_content_container()
.pb_1()
.border_t_1() .border_t_1()
.border_color(self.tool_card_border_color(cx)) .border_color(self.tool_card_border_color(cx))
.child( .child(
Icon::new(IconName::ArrowCircle) Label::new("Result")
.size(IconSize::Small)
.color(Color::Accent)
.with_animation(
"arrow-circle",
Animation::new(Duration::from_secs(2)).repeat(),
|icon, delta| {
icon.transform(Transformation::rotate(percentage(
delta,
)))
},
),
)
.child(
Label::new("Running…")
.size(LabelSize::XSmall) .size(LabelSize::XSmall)
.color(Color::Muted) .color(Color::Muted)
.buffer_font(cx), .buffer_font(cx),
)
.child(div().w_full().text_ui_sm(cx).children(
rendered_tool_use.as_ref().map(|rendered| {
MarkdownElement::new(
rendered.output.clone(),
tool_use_markdown_style(window, cx),
)
}),
)),
),
ToolUseStatus::Running => container.child(
results_content_container().child(
h_flex()
.gap_1()
.pb_1()
.border_t_1()
.border_color(self.tool_card_border_color(cx))
.child(
Icon::new(IconName::ArrowCircle)
.size(IconSize::Small)
.color(Color::Accent)
.with_animation(
"arrow-circle",
Animation::new(Duration::from_secs(2)).repeat(),
|icon, delta| {
icon.transform(Transformation::rotate(percentage(
delta,
)))
},
),
)
.child(
Label::new("Running…")
.size(LabelSize::XSmall)
.color(Color::Muted)
.buffer_font(cx),
),
),
),
ToolUseStatus::Error(_) => {
container.child(
results_content_container()
.border_t_1()
.border_color(self.tool_card_border_color(cx))
.child(
Label::new("Error")
.size(LabelSize::XSmall)
.color(Color::Muted)
.buffer_font(cx),
)
.child(div().text_ui_sm(cx).children(
rendered_tool_use.as_ref().map(|rendered| {
MarkdownElement::new(
rendered.output.clone(),
tool_use_markdown_style(window, cx),
)
}),
)),
)
}
ToolUseStatus::Pending => container,
ToolUseStatus::NeedsConfirmation => container.child(
results_content_container()
.border_t_1()
.border_color(self.tool_card_border_color(cx))
.child(
Label::new("Asking Permission")
.size(LabelSize::Small)
.color(Color::Muted)
.buffer_font(cx),
), ),
), ),
), });
ToolUseStatus::Error(_) => container.child(
results_content_container()
.border_t_1()
.border_color(self.tool_card_border_color(cx))
.child(
Label::new("Error")
.size(LabelSize::XSmall)
.color(Color::Muted)
.buffer_font(cx),
)
.child(
div().text_ui_sm(cx).children(
rendered_tool_use
.as_ref()
.map(|rendered| rendered.output.clone()),
),
),
),
ToolUseStatus::Pending => container,
ToolUseStatus::NeedsConfirmation => container.child(
results_content_container()
.border_t_1()
.border_color(self.tool_card_border_color(cx))
.child(
Label::new("Asking Permission")
.size(LabelSize::Small)
.color(Color::Muted)
.buffer_font(cx),
),
),
});
let gradient_overlay = |color: Hsla| { let gradient_overlay = |color: Hsla| {
div() div()
@ -1948,7 +1939,7 @@ impl ActiveThread {
) )
.child( .child(
h_flex().pr_8().text_ui_sm(cx).children( h_flex().pr_8().text_ui_sm(cx).children(
rendered_tool_use.map(|rendered| rendered.label) rendered_tool_use.map(|rendered| MarkdownElement::new(rendered.label, tool_use_markdown_style(window, cx)))
), ),
), ),
) )
@ -2036,7 +2027,7 @@ impl ActiveThread {
) )
.child( .child(
h_flex().pr_8().text_ui_sm(cx).children( h_flex().pr_8().text_ui_sm(cx).children(
rendered_tool_use.map(|rendered| rendered.label) rendered_tool_use.map(|rendered| MarkdownElement::new(rendered.label, tool_use_markdown_style(window, cx)))
), ),
), ),
) )

View file

@ -7,7 +7,7 @@ use gpui::{
}; };
use language::Buffer; use language::Buffer;
use language::CodeLabel; use language::CodeLabel;
use markdown::Markdown; use markdown::{Markdown, MarkdownElement};
use multi_buffer::{Anchor, ExcerptId}; use multi_buffer::{Anchor, ExcerptId};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use project::CompletionSource; use project::CompletionSource;
@ -622,21 +622,18 @@ impl CompletionsMenu {
let language = editor let language = editor
.language_at(self.initial_position, cx) .language_at(self.initial_position, cx)
.map(|l| l.name().to_proto()); .map(|l| l.name().to_proto());
Markdown::new( Markdown::new(SharedString::default(), languages, language, cx)
SharedString::default(), .copy_code_block_buttons(false)
hover_markdown_style(window, cx), .open_url(open_markdown_url)
languages,
language,
cx,
)
.copy_code_block_buttons(false)
.open_url(open_markdown_url)
}) })
}); });
markdown.update(cx, |markdown, cx| { markdown.update(cx, |markdown, cx| {
markdown.reset(parsed.clone(), cx); markdown.reset(parsed.clone(), cx);
}); });
div().child(markdown.clone()) div().child(MarkdownElement::new(
markdown.clone(),
hover_markdown_style(window, cx),
))
} }
CompletionDocumentation::MultiLineMarkdown(_) => return None, CompletionDocumentation::MultiLineMarkdown(_) => return None,
CompletionDocumentation::SingleLine(_) => return None, CompletionDocumentation::SingleLine(_) => return None,

View file

@ -3912,9 +3912,13 @@ impl EditorElement {
); );
let hover_popovers = self.editor.update(cx, |editor, cx| { let hover_popovers = self.editor.update(cx, |editor, cx| {
editor editor.hover_state.render(
.hover_state snapshot,
.render(snapshot, visible_display_row_range.clone(), max_size, cx) visible_display_row_range.clone(),
max_size,
window,
cx,
)
}); });
let Some((position, hover_popovers)) = hover_popovers else { let Some((position, hover_popovers)) = hover_popovers else {
return; return;

View file

@ -14,7 +14,7 @@ use gpui::{
use itertools::Itertools; use itertools::Itertools;
use language::{DiagnosticEntry, Language, LanguageRegistry}; use language::{DiagnosticEntry, Language, LanguageRegistry};
use lsp::DiagnosticSeverity; use lsp::DiagnosticSeverity;
use markdown::{Markdown, MarkdownStyle}; use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use multi_buffer::{MultiOrSingleBufferOffsetRange, ToOffset}; use multi_buffer::{MultiOrSingleBufferOffsetRange, ToOffset};
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart}; use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart};
use settings::Settings; use settings::Settings;
@ -310,7 +310,7 @@ fn show_hover(
let mut background_color: Option<Hsla> = None; let mut background_color: Option<Hsla> = None;
let parsed_content = cx let parsed_content = cx
.new_window_entity(|window, cx| { .new_window_entity(|_window, cx| {
let status_colors = cx.theme().status(); let status_colors = cx.theme().status();
match local_diagnostic.diagnostic.severity { match local_diagnostic.diagnostic.severity {
@ -335,32 +335,8 @@ fn show_hover(
border_color = Some(status_colors.ignored_border); border_color = Some(status_colors.ignored_border);
} }
}; };
let settings = ThemeSettings::get_global(cx);
let mut base_text_style = window.text_style();
base_text_style.refine(&TextStyleRefinement {
font_family: Some(settings.ui_font.family.clone()),
font_fallbacks: settings.ui_font.fallbacks.clone(),
font_size: Some(settings.ui_font_size(cx).into()),
color: Some(cx.theme().colors().editor_foreground),
background_color: Some(gpui::transparent_black()),
..Default::default() Markdown::new_text(SharedString::new(text), cx).open_url(open_markdown_url)
});
let markdown_style = MarkdownStyle {
base_text_style,
selection_background_color: { cx.theme().players().local().selection },
link: TextStyleRefinement {
underline: Some(gpui::UnderlineStyle {
thickness: px(1.),
color: Some(cx.theme().colors().editor_foreground),
wavy: false,
}),
..Default::default()
},
..Default::default()
};
Markdown::new_text(SharedString::new(text), markdown_style.clone(), cx)
.open_url(open_markdown_url)
}) })
.ok(); .ok();
@ -563,10 +539,9 @@ async fn parse_blocks(
.join("\n\n"); .join("\n\n");
let rendered_block = cx let rendered_block = cx
.new_window_entity(|window, cx| { .new_window_entity(|_window, cx| {
Markdown::new( Markdown::new(
combined_text.into(), combined_text.into(),
hover_markdown_style(window, cx),
Some(language_registry.clone()), Some(language_registry.clone()),
fallback_language_name, fallback_language_name,
cx, cx,
@ -704,6 +679,7 @@ impl HoverState {
snapshot: &EditorSnapshot, snapshot: &EditorSnapshot,
visible_rows: Range<DisplayRow>, visible_rows: Range<DisplayRow>,
max_size: Size<Pixels>, max_size: Size<Pixels>,
window: &mut Window,
cx: &mut Context<Editor>, cx: &mut Context<Editor>,
) -> Option<(DisplayPoint, Vec<AnyElement>)> { ) -> Option<(DisplayPoint, Vec<AnyElement>)> {
// If there is a diagnostic, position the popovers based on that. // If there is a diagnostic, position the popovers based on that.
@ -738,10 +714,10 @@ impl HoverState {
let mut elements = Vec::new(); let mut elements = Vec::new();
if let Some(diagnostic_popover) = self.diagnostic_popover.as_ref() { if let Some(diagnostic_popover) = self.diagnostic_popover.as_ref() {
elements.push(diagnostic_popover.render(max_size, cx)); elements.push(diagnostic_popover.render(max_size, window, cx));
} }
for info_popover in &mut self.info_popovers { for info_popover in &mut self.info_popovers {
elements.push(info_popover.render(max_size, cx)); elements.push(info_popover.render(max_size, window, cx));
} }
Some((point, elements)) Some((point, elements))
@ -781,6 +757,7 @@ impl InfoPopover {
pub(crate) fn render( pub(crate) fn render(
&mut self, &mut self,
max_size: Size<Pixels>, max_size: Size<Pixels>,
window: &mut Window,
cx: &mut Context<Editor>, cx: &mut Context<Editor>,
) -> AnyElement { ) -> AnyElement {
let keyboard_grace = Rc::clone(&self.keyboard_grace); let keyboard_grace = Rc::clone(&self.keyboard_grace);
@ -806,7 +783,10 @@ impl InfoPopover {
.max_h(max_size.height) .max_h(max_size.height)
.p_2() .p_2()
.track_scroll(&self.scroll_handle) .track_scroll(&self.scroll_handle)
.child(markdown.clone()), .child(MarkdownElement::new(
markdown.clone(),
hover_markdown_style(window, cx),
)),
) )
.child(self.render_vertical_scrollbar(cx)); .child(self.render_vertical_scrollbar(cx));
} }
@ -868,11 +848,41 @@ pub struct DiagnosticPopover {
} }
impl DiagnosticPopover { impl DiagnosticPopover {
pub fn render(&self, max_size: Size<Pixels>, cx: &mut Context<Editor>) -> AnyElement { pub fn render(
&self,
max_size: Size<Pixels>,
window: &mut Window,
cx: &mut Context<Editor>,
) -> AnyElement {
let keyboard_grace = Rc::clone(&self.keyboard_grace); let keyboard_grace = Rc::clone(&self.keyboard_grace);
let mut markdown_div = div().py_1().px_2(); let mut markdown_div = div().py_1().px_2();
if let Some(markdown) = &self.parsed_content { if let Some(markdown) = &self.parsed_content {
markdown_div = markdown_div.child(markdown.clone()); let settings = ThemeSettings::get_global(cx);
let mut base_text_style = window.text_style();
base_text_style.refine(&TextStyleRefinement {
font_family: Some(settings.ui_font.family.clone()),
font_fallbacks: settings.ui_font.fallbacks.clone(),
font_size: Some(settings.ui_font_size(cx).into()),
color: Some(cx.theme().colors().editor_foreground),
background_color: Some(gpui::transparent_black()),
..Default::default()
});
let markdown_style = MarkdownStyle {
base_text_style,
selection_background_color: { cx.theme().players().local().selection },
link: TextStyleRefinement {
underline: Some(gpui::UnderlineStyle {
thickness: px(1.),
color: Some(cx.theme().colors().editor_foreground),
wavy: false,
}),
..Default::default()
},
..Default::default()
};
markdown_div =
markdown_div.child(MarkdownElement::new(markdown.clone(), markdown_style));
} }
if let Some(background_color) = &self.background_color { if let Some(background_color) = &self.background_color {

View file

@ -95,14 +95,13 @@ impl BlameRenderer for GitBlameRenderer {
) )
} }
}) })
.hoverable_tooltip(move |window, cx| { .hoverable_tooltip(move |_window, cx| {
cx.new(|cx| { cx.new(|cx| {
CommitTooltip::blame_entry( CommitTooltip::blame_entry(
&blame_entry, &blame_entry,
details.clone(), details.clone(),
repository.clone(), repository.clone(),
workspace.clone(), workspace.clone(),
window,
cx, cx,
) )
}) })
@ -145,14 +144,13 @@ impl BlameRenderer for GitBlameRenderer {
.child(Icon::new(IconName::FileGit).color(Color::Hint)) .child(Icon::new(IconName::FileGit).color(Color::Hint))
.child(text) .child(text)
.gap_2() .gap_2()
.hoverable_tooltip(move |window, cx| { .hoverable_tooltip(move |_window, cx| {
let tooltip = cx.new(|cx| { let tooltip = cx.new(|cx| {
CommitTooltip::blame_entry( CommitTooltip::blame_entry(
&blame_entry, &blame_entry,
details.clone(), details.clone(),
repository.clone(), repository.clone(),
workspace.clone(), workspace.clone(),
window,
cx, cx,
) )
}); });

View file

@ -8,7 +8,7 @@ use gpui::{
App, Asset, ClipboardItem, Element, Entity, MouseButton, ParentElement, Render, ScrollHandle, App, Asset, ClipboardItem, Element, Entity, MouseButton, ParentElement, Render, ScrollHandle,
StatefulInteractiveElement, WeakEntity, prelude::*, StatefulInteractiveElement, WeakEntity, prelude::*,
}; };
use markdown::Markdown; use markdown::{Markdown, MarkdownElement};
use project::git_store::Repository; use project::git_store::Repository;
use settings::Settings; use settings::Settings;
use std::hash::Hash; use std::hash::Hash;
@ -118,7 +118,6 @@ impl CommitTooltip {
details: Option<ParsedCommitMessage>, details: Option<ParsedCommitMessage>,
repository: Entity<Repository>, repository: Entity<Repository>,
workspace: WeakEntity<Workspace>, workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Self { ) -> Self {
let commit_time = blame let commit_time = blame
@ -140,7 +139,6 @@ impl CommitTooltip {
}, },
repository, repository,
workspace, workspace,
window,
cx, cx,
) )
} }
@ -149,13 +147,8 @@ impl CommitTooltip {
commit: CommitDetails, commit: CommitDetails,
repository: Entity<Repository>, repository: Entity<Repository>,
workspace: WeakEntity<Workspace>, workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Self { ) -> Self {
let mut style = hover_markdown_style(window, cx);
if let Some(code_block) = &style.code_block.text {
style.base_text_style.refine(code_block);
}
let markdown = cx.new(|cx| { let markdown = cx.new(|cx| {
Markdown::new( Markdown::new(
commit commit
@ -163,7 +156,6 @@ impl CommitTooltip {
.as_ref() .as_ref()
.map(|message| message.message.clone()) .map(|message| message.message.clone())
.unwrap_or_default(), .unwrap_or_default(),
style,
None, None,
None, None,
cx, cx,
@ -199,12 +191,19 @@ impl Render for CommitTooltip {
OffsetDateTime::now_utc(), OffsetDateTime::now_utc(),
time_format::TimestampFormat::MediumAbsolute, time_format::TimestampFormat::MediumAbsolute,
); );
let markdown_style = {
let mut style = hover_markdown_style(window, cx);
if let Some(code_block) = &style.code_block.text {
style.base_text_style.refine(code_block);
}
style
};
let message = self let message = self
.commit .commit
.message .message
.as_ref() .as_ref()
.map(|_| self.markdown.clone().into_any_element()) .map(|_| MarkdownElement::new(self.markdown.clone(), markdown_style).into_any())
.unwrap_or("<no commit message>".into_any()); .unwrap_or("<no commit message>".into_any());
let pull_request = self let pull_request = self

View file

@ -3927,9 +3927,9 @@ impl GitPanelMessageTooltip {
}), }),
}; };
this.update_in(cx, |this: &mut GitPanelMessageTooltip, window, cx| { this.update(cx, |this: &mut GitPanelMessageTooltip, cx| {
this.commit_tooltip = Some(cx.new(move |cx| { this.commit_tooltip = Some(cx.new(move |cx| {
CommitTooltip::new(commit_details, repository, workspace, window, cx) CommitTooltip::new(commit_details, repository, workspace, cx)
})); }));
cx.notify(); cx.notify();
}) })

View file

@ -1,7 +1,7 @@
use assets::Assets; use assets::Assets;
use gpui::{Application, Entity, KeyBinding, StyleRefinement, WindowOptions, prelude::*, rgb}; use gpui::{Application, Entity, KeyBinding, StyleRefinement, WindowOptions, prelude::*, rgb};
use language::{LanguageRegistry, language_settings::AllLanguageSettings}; use language::{LanguageRegistry, language_settings::AllLanguageSettings};
use markdown::{Markdown, MarkdownStyle}; use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use settings::SettingsStore; use settings::SettingsStore;
use std::sync::Arc; use std::sync::Arc;
@ -47,54 +47,7 @@ pub fn main() {
cx.activate(true); cx.activate(true);
cx.open_window(WindowOptions::default(), |_, cx| { cx.open_window(WindowOptions::default(), |_, cx| {
cx.new(|cx| { cx.new(|cx| MarkdownExample::new(MARKDOWN_EXAMPLE.into(), language_registry, cx))
let markdown_style = MarkdownStyle {
base_text_style: gpui::TextStyle {
font_family: "Zed Plex Sans".into(),
color: cx.theme().colors().terminal_ansi_black,
..Default::default()
},
code_block: StyleRefinement::default()
.font_family("Zed Plex Mono")
.m(rems(1.))
.bg(rgb(0xAAAAAAA)),
inline_code: gpui::TextStyleRefinement {
font_family: Some("Zed Mono".into()),
color: Some(cx.theme().colors().editor_foreground),
background_color: Some(cx.theme().colors().editor_background),
..Default::default()
},
rule_color: Color::Muted.color(cx),
block_quote_border_color: Color::Muted.color(cx),
block_quote: gpui::TextStyleRefinement {
color: Some(Color::Muted.color(cx)),
..Default::default()
},
link: gpui::TextStyleRefinement {
color: Some(Color::Accent.color(cx)),
underline: Some(gpui::UnderlineStyle {
thickness: px(1.),
color: Some(Color::Accent.color(cx)),
wavy: false,
}),
..Default::default()
},
syntax: cx.theme().syntax().clone(),
selection_background_color: {
let mut selection = cx.theme().players().local().selection;
selection.fade_out(0.7);
selection
},
..Default::default()
};
MarkdownExample::new(
MARKDOWN_EXAMPLE.into(),
markdown_style,
language_registry,
cx,
)
})
}) })
.unwrap(); .unwrap();
}); });
@ -105,16 +58,10 @@ struct MarkdownExample {
} }
impl MarkdownExample { impl MarkdownExample {
pub fn new( pub fn new(text: SharedString, language_registry: Arc<LanguageRegistry>, cx: &mut App) -> Self {
text: SharedString,
style: MarkdownStyle,
language_registry: Arc<LanguageRegistry>,
cx: &mut App,
) -> Self {
let markdown = cx.new(|cx| { let markdown = cx.new(|cx| {
Markdown::new( Markdown::new(
text, text,
style,
Some(language_registry), Some(language_registry),
Some("TypeScript".to_string()), Some("TypeScript".to_string()),
cx, cx,
@ -125,7 +72,47 @@ impl MarkdownExample {
} }
impl Render for MarkdownExample { impl Render for MarkdownExample {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let markdown_style = MarkdownStyle {
base_text_style: gpui::TextStyle {
font_family: "Zed Plex Sans".into(),
color: cx.theme().colors().terminal_ansi_black,
..Default::default()
},
code_block: StyleRefinement::default()
.font_family("Zed Plex Mono")
.m(rems(1.))
.bg(rgb(0xAAAAAAA)),
inline_code: gpui::TextStyleRefinement {
font_family: Some("Zed Mono".into()),
color: Some(cx.theme().colors().editor_foreground),
background_color: Some(cx.theme().colors().editor_background),
..Default::default()
},
rule_color: Color::Muted.color(cx),
block_quote_border_color: Color::Muted.color(cx),
block_quote: gpui::TextStyleRefinement {
color: Some(Color::Muted.color(cx)),
..Default::default()
},
link: gpui::TextStyleRefinement {
color: Some(Color::Accent.color(cx)),
underline: Some(gpui::UnderlineStyle {
thickness: px(1.),
color: Some(Color::Accent.color(cx)),
wavy: false,
}),
..Default::default()
},
syntax: cx.theme().syntax().clone(),
selection_background_color: {
let mut selection = cx.theme().players().local().selection;
selection.fade_out(0.7);
selection
},
..Default::default()
};
div() div()
.id("markdown-example") .id("markdown-example")
.debug_selector(|| "foo".into()) .debug_selector(|| "foo".into())
@ -134,6 +121,6 @@ impl Render for MarkdownExample {
.size_full() .size_full()
.p_4() .p_4()
.overflow_y_scroll() .overflow_y_scroll()
.child(self.markdown.clone()) .child(MarkdownElement::new(self.markdown.clone(), markdown_style))
} }
} }

View file

@ -1,7 +1,7 @@
use assets::Assets; use assets::Assets;
use gpui::{Application, Entity, KeyBinding, Length, StyleRefinement, WindowOptions, rgb}; use gpui::{Application, Entity, KeyBinding, Length, StyleRefinement, WindowOptions, rgb};
use language::{LanguageRegistry, language_settings::AllLanguageSettings}; use language::{LanguageRegistry, language_settings::AllLanguageSettings};
use markdown::{Markdown, MarkdownStyle}; use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use settings::SettingsStore; use settings::SettingsStore;
use std::sync::Arc; use std::sync::Arc;
@ -37,58 +37,7 @@ pub fn main() {
cx.activate(true); cx.activate(true);
let _ = cx.open_window(WindowOptions::default(), |_, cx| { let _ = cx.open_window(WindowOptions::default(), |_, cx| {
cx.new(|cx| { cx.new(|cx| {
let markdown_style = MarkdownStyle { let markdown = cx.new(|cx| Markdown::new(MARKDOWN_EXAMPLE.into(), None, None, cx));
base_text_style: gpui::TextStyle {
font_family: "Zed Mono".into(),
color: cx.theme().colors().text,
..Default::default()
},
code_block: StyleRefinement {
text: Some(gpui::TextStyleRefinement {
font_family: Some("Zed Mono".into()),
background_color: Some(cx.theme().colors().editor_background),
..Default::default()
}),
margin: gpui::EdgesRefinement {
top: Some(Length::Definite(rems(4.).into())),
left: Some(Length::Definite(rems(4.).into())),
right: Some(Length::Definite(rems(4.).into())),
bottom: Some(Length::Definite(rems(4.).into())),
},
..Default::default()
},
inline_code: gpui::TextStyleRefinement {
font_family: Some("Zed Mono".into()),
background_color: Some(cx.theme().colors().editor_background),
..Default::default()
},
rule_color: Color::Muted.color(cx),
block_quote_border_color: Color::Muted.color(cx),
block_quote: gpui::TextStyleRefinement {
color: Some(Color::Muted.color(cx)),
..Default::default()
},
link: gpui::TextStyleRefinement {
color: Some(Color::Accent.color(cx)),
underline: Some(gpui::UnderlineStyle {
thickness: px(1.),
color: Some(Color::Accent.color(cx)),
wavy: false,
}),
..Default::default()
},
syntax: cx.theme().syntax().clone(),
selection_background_color: {
let mut selection = cx.theme().players().local().selection;
selection.fade_out(0.7);
selection
},
heading: Default::default(),
..Default::default()
};
let markdown = cx.new(|cx| {
Markdown::new(MARKDOWN_EXAMPLE.into(), markdown_style, None, None, cx)
});
HelloWorld { markdown } HelloWorld { markdown }
}) })
@ -100,7 +49,57 @@ struct HelloWorld {
} }
impl Render for HelloWorld { impl Render for HelloWorld {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let markdown_style = MarkdownStyle {
base_text_style: gpui::TextStyle {
font_family: "Zed Mono".into(),
color: cx.theme().colors().text,
..Default::default()
},
code_block: StyleRefinement {
text: Some(gpui::TextStyleRefinement {
font_family: Some("Zed Mono".into()),
background_color: Some(cx.theme().colors().editor_background),
..Default::default()
}),
margin: gpui::EdgesRefinement {
top: Some(Length::Definite(rems(4.).into())),
left: Some(Length::Definite(rems(4.).into())),
right: Some(Length::Definite(rems(4.).into())),
bottom: Some(Length::Definite(rems(4.).into())),
},
..Default::default()
},
inline_code: gpui::TextStyleRefinement {
font_family: Some("Zed Mono".into()),
background_color: Some(cx.theme().colors().editor_background),
..Default::default()
},
rule_color: Color::Muted.color(cx),
block_quote_border_color: Color::Muted.color(cx),
block_quote: gpui::TextStyleRefinement {
color: Some(Color::Muted.color(cx)),
..Default::default()
},
link: gpui::TextStyleRefinement {
color: Some(Color::Accent.color(cx)),
underline: Some(gpui::UnderlineStyle {
thickness: px(1.),
color: Some(Color::Accent.color(cx)),
wavy: false,
}),
..Default::default()
},
syntax: cx.theme().syntax().clone(),
selection_background_color: {
let mut selection = cx.theme().players().local().selection;
selection.fade_out(0.7);
selection
},
heading: Default::default(),
..Default::default()
};
div() div()
.flex() .flex()
.bg(rgb(0x2e7d32)) .bg(rgb(0x2e7d32))
@ -112,6 +111,10 @@ impl Render for HelloWorld {
.border_color(rgb(0x0000ff)) .border_color(rgb(0x0000ff))
.text_xl() .text_xl()
.text_color(rgb(0xffffff)) .text_color(rgb(0xffffff))
.child(div().child(self.markdown.clone()).p_20()) .child(
div()
.child(MarkdownElement::new(self.markdown.clone(), markdown_style))
.p_20(),
)
} }
} }

View file

@ -14,7 +14,7 @@ use std::time::Duration;
use gpui::{ use gpui::{
AnyElement, App, BorderStyle, Bounds, ClipboardItem, CursorStyle, DispatchPhase, Edges, Entity, AnyElement, App, BorderStyle, Bounds, ClipboardItem, CursorStyle, DispatchPhase, Edges, Entity,
FocusHandle, Focusable, FontStyle, FontWeight, GlobalElementId, Hitbox, Hsla, KeyContext, FocusHandle, Focusable, FontStyle, FontWeight, GlobalElementId, Hitbox, Hsla, KeyContext,
Length, MouseDownEvent, MouseEvent, MouseMoveEvent, MouseUpEvent, Point, Render, Stateful, Length, MouseDownEvent, MouseEvent, MouseMoveEvent, MouseUpEvent, Point, Stateful,
StrikethroughStyle, StyleRefinement, StyledText, Task, TextLayout, TextRun, TextStyle, StrikethroughStyle, StyleRefinement, StyledText, Task, TextLayout, TextRun, TextStyle,
TextStyleRefinement, actions, point, quad, TextStyleRefinement, actions, point, quad,
}; };
@ -74,7 +74,6 @@ pub struct Markdown {
selection: Selection, selection: Selection,
pressed_link: Option<RenderedLink>, pressed_link: Option<RenderedLink>,
autoscroll_request: Option<usize>, autoscroll_request: Option<usize>,
style: MarkdownStyle,
parsed_markdown: ParsedMarkdown, parsed_markdown: ParsedMarkdown,
should_reparse: bool, should_reparse: bool,
pending_parse: Option<Task<Option<()>>>, pending_parse: Option<Task<Option<()>>>,
@ -97,7 +96,6 @@ actions!(markdown, [Copy]);
impl Markdown { impl Markdown {
pub fn new( pub fn new(
source: SharedString, source: SharedString,
style: MarkdownStyle,
language_registry: Option<Arc<LanguageRegistry>>, language_registry: Option<Arc<LanguageRegistry>>,
fallback_code_block_language: Option<String>, fallback_code_block_language: Option<String>,
cx: &mut Context<Self>, cx: &mut Context<Self>,
@ -108,7 +106,6 @@ impl Markdown {
selection: Selection::default(), selection: Selection::default(),
pressed_link: None, pressed_link: None,
autoscroll_request: None, autoscroll_request: None,
style,
should_reparse: false, should_reparse: false,
parsed_markdown: ParsedMarkdown::default(), parsed_markdown: ParsedMarkdown::default(),
pending_parse: None, pending_parse: None,
@ -136,14 +133,13 @@ impl Markdown {
} }
} }
pub fn new_text(source: SharedString, style: MarkdownStyle, cx: &mut Context<Self>) -> Self { pub fn new_text(source: SharedString, cx: &mut Context<Self>) -> Self {
let focus_handle = cx.focus_handle(); let focus_handle = cx.focus_handle();
let mut this = Self { let mut this = Self {
source, source,
selection: Selection::default(), selection: Selection::default(),
pressed_link: None, pressed_link: None,
autoscroll_request: None, autoscroll_request: None,
style,
should_reparse: false, should_reparse: false,
parsed_markdown: ParsedMarkdown::default(), parsed_markdown: ParsedMarkdown::default(),
pending_parse: None, pending_parse: None,
@ -275,12 +271,6 @@ impl Markdown {
} }
} }
impl Render for Markdown {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
MarkdownElement::new(cx.entity().clone(), self.style.clone())
}
}
impl Focusable for Markdown { impl Focusable for Markdown {
fn focus_handle(&self, _cx: &App) -> FocusHandle { fn focus_handle(&self, _cx: &App) -> FocusHandle {
self.focus_handle.clone() self.focus_handle.clone()
@ -341,7 +331,7 @@ pub struct MarkdownElement {
} }
impl MarkdownElement { impl MarkdownElement {
fn new(markdown: Entity<Markdown>, style: MarkdownStyle) -> Self { pub fn new(markdown: Entity<Markdown>, style: MarkdownStyle) -> Self {
Self { markdown, style } Self { markdown, style }
} }
@ -638,6 +628,10 @@ impl Element for MarkdownElement {
// If the path actually exists in the project, render a link to it. // If the path actually exists in the project, render a link to it.
if let Some(project_path) = if let Some(project_path) =
window.root::<Workspace>().flatten().and_then(|workspace| { window.root::<Workspace>().flatten().and_then(|workspace| {
if path_range.path.is_absolute() {
return None;
}
workspace workspace
.read(cx) .read(cx)
.project() .project()

View file

@ -13,7 +13,7 @@ use gpui::{
}; };
use language::CursorShape; use language::CursorShape;
use markdown::{Markdown, MarkdownStyle}; use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use release_channel::ReleaseChannel; use release_channel::ReleaseChannel;
use remote::ssh_session::{ConnectionIdentifier, SshPortForwardOption}; use remote::ssh_session::{ConnectionIdentifier, SshPortForwardOption};
use remote::{SshConnectionOptions, SshPlatform, SshRemoteClient}; use remote::{SshConnectionOptions, SshPlatform, SshRemoteClient};
@ -182,7 +182,6 @@ impl SshPrompt {
) { ) {
let theme = ThemeSettings::get_global(cx); let theme = ThemeSettings::get_global(cx);
let mut text_style = window.text_style();
let refinement = TextStyleRefinement { let refinement = TextStyleRefinement {
font_family: Some(theme.buffer_font.family.clone()), font_family: Some(theme.buffer_font.family.clone()),
font_features: Some(FontFeatures::disable_ligatures()), font_features: Some(FontFeatures::disable_ligatures()),
@ -192,7 +191,6 @@ impl SshPrompt {
..Default::default() ..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);
@ -202,12 +200,8 @@ impl SshPrompt {
editor.set_text_style_refinement(refinement); editor.set_text_style_refinement(refinement);
editor.set_cursor_shape(CursorShape::Block, cx); editor.set_cursor_shape(CursorShape::Block, cx);
}); });
let markdown_style = MarkdownStyle {
base_text_style: text_style, let markdown = cx.new(|cx| Markdown::new_text(prompt.into(), cx));
selection_background_color: cx.theme().players().local().selection,
..Default::default()
};
let markdown = cx.new(|cx| Markdown::new_text(prompt.into(), markdown_style, cx));
self.prompt = Some((markdown, tx)); self.prompt = Some((markdown, tx));
self.status_message.take(); self.status_message.take();
window.focus(&self.editor.focus_handle(cx)); window.focus(&self.editor.focus_handle(cx));
@ -231,7 +225,26 @@ impl SshPrompt {
} }
impl Render for SshPrompt { impl Render for SshPrompt {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = ThemeSettings::get_global(cx);
let mut text_style = window.text_style();
let refinement = TextStyleRefinement {
font_family: Some(theme.buffer_font.family.clone()),
font_features: Some(FontFeatures::disable_ligatures()),
font_size: Some(theme.buffer_font_size(cx).into()),
color: Some(cx.theme().colors().editor_foreground),
background_color: Some(gpui::transparent_black()),
..Default::default()
};
text_style.refine(&refinement);
let markdown_style = MarkdownStyle {
base_text_style: text_style,
selection_background_color: cx.theme().players().local().selection,
..Default::default()
};
v_flex() v_flex()
.key_context("PasswordPrompt") .key_context("PasswordPrompt")
.py_2() .py_2()
@ -266,7 +279,7 @@ impl Render for SshPrompt {
div() div()
.size_full() .size_full()
.overflow_hidden() .overflow_hidden()
.child(prompt.0.clone()) .child(MarkdownElement::new(prompt.0.clone(), markdown_style))
.child(self.editor.clone()), .child(self.editor.clone()),
) )
}) })

View file

@ -4,7 +4,7 @@ use gpui::{
Refineable, Render, RenderablePromptHandle, SharedString, Styled, TextStyleRefinement, Window, Refineable, Render, RenderablePromptHandle, SharedString, Styled, TextStyleRefinement, Window,
div, div,
}; };
use markdown::{Markdown, MarkdownStyle}; use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use settings::{Settings, SettingsStore}; use settings::{Settings, SettingsStore};
use theme::ThemeSettings; use theme::ThemeSettings;
use ui::{ use ui::{
@ -47,24 +47,9 @@ fn zed_prompt_renderer(
actions: actions.iter().map(ToString::to_string).collect(), actions: actions.iter().map(ToString::to_string).collect(),
focus: cx.focus_handle(), focus: cx.focus_handle(),
active_action_id: 0, active_action_id: 0,
detail: detail.filter(|text| !text.is_empty()).map(|text| { detail: detail
cx.new(|cx| { .filter(|text| !text.is_empty())
let settings = ThemeSettings::get_global(cx); .map(|text| cx.new(|cx| Markdown::new(SharedString::new(text), None, None, cx))),
let mut base_text_style = window.text_style();
base_text_style.refine(&TextStyleRefinement {
font_family: Some(settings.ui_font.family.clone()),
font_size: Some(settings.ui_font_size(cx).into()),
color: Some(ui::Color::Muted.color(cx)),
..Default::default()
});
let markdown_style = MarkdownStyle {
base_text_style,
selection_background_color: { cx.theme().players().local().selection },
..Default::default()
};
Markdown::new(SharedString::new(text), markdown_style, None, None, cx)
})
}),
} }
}); });
@ -127,7 +112,7 @@ impl ZedPromptRenderer {
} }
impl Render for ZedPromptRenderer { impl Render for ZedPromptRenderer {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx); let settings = ThemeSettings::get_global(cx);
let font_family = settings.ui_font.family.clone(); let font_family = settings.ui_font.family.clone();
let prompt = v_flex() let prompt = v_flex()
@ -153,11 +138,26 @@ impl Render for ZedPromptRenderer {
.child(self.message.clone()) .child(self.message.clone())
.text_color(ui::Color::Default.color(cx)), .text_color(ui::Color::Default.color(cx)),
) )
.children( .children(self.detail.clone().map(|detail| {
self.detail div()
.clone() .w_full()
.map(|detail| div().w_full().text_xs().child(detail)), .text_xs()
) .child(MarkdownElement::new(detail, {
let settings = ThemeSettings::get_global(cx);
let mut base_text_style = window.text_style();
base_text_style.refine(&TextStyleRefinement {
font_family: Some(settings.ui_font.family.clone()),
font_size: Some(settings.ui_font_size(cx).into()),
color: Some(ui::Color::Muted.color(cx)),
..Default::default()
});
MarkdownStyle {
base_text_style,
selection_background_color: { cx.theme().players().local().selection },
..Default::default()
}
}))
}))
.child(h_flex().justify_end().gap_2().children( .child(h_flex().justify_end().gap_2().children(
self.actions.iter().enumerate().rev().map(|(ix, action)| { self.actions.iter().enumerate().rev().map(|(ix, action)| {
ui::Button::new(ix, action.clone()) ui::Button::new(ix, action.clone())