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_model::{ConfiguredModel, LanguageModelRegistry, LanguageModelToolUseId, Role};
use markdown::{Markdown, MarkdownStyle};
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use project::ProjectItem as _;
use settings::{Settings as _, update_settings_file};
use std::rc::Rc;
@ -77,7 +77,6 @@ impl RenderedMessage {
segments: &[MessageSegment],
language_registry: Arc<LanguageRegistry>,
workspace: WeakEntity<Workspace>,
window: &Window,
cx: &mut App,
) -> Self {
let mut this = Self {
@ -85,18 +84,12 @@ impl RenderedMessage {
segments: Vec::with_capacity(segments.len()),
};
for segment in segments {
this.push_segment(segment, workspace.clone(), window, cx);
this.push_segment(segment, workspace.clone(), cx);
}
this
}
fn append_thinking(
&mut self,
text: &String,
workspace: WeakEntity<Workspace>,
window: &Window,
cx: &mut App,
) {
fn append_thinking(&mut self, text: &String, workspace: WeakEntity<Workspace>, cx: &mut App) {
if let Some(RenderedMessageSegment::Thinking {
content,
scroll_handle,
@ -112,7 +105,6 @@ impl RenderedMessage {
text.into(),
self.language_registry.clone(),
workspace,
window,
cx,
),
scroll_handle: ScrollHandle::default(),
@ -120,13 +112,7 @@ impl RenderedMessage {
}
}
fn append_text(
&mut self,
text: &String,
workspace: WeakEntity<Workspace>,
window: &Window,
cx: &mut App,
) {
fn append_text(&mut self, text: &String, workspace: WeakEntity<Workspace>, cx: &mut App) {
if let Some(RenderedMessageSegment::Text(markdown)) = self.segments.last_mut() {
markdown.update(cx, |markdown, cx| markdown.append(text, cx));
} else {
@ -135,7 +121,6 @@ impl RenderedMessage {
SharedString::from(text),
self.language_registry.clone(),
workspace,
window,
cx,
)));
}
@ -145,7 +130,6 @@ impl RenderedMessage {
&mut self,
segment: &MessageSegment,
workspace: WeakEntity<Workspace>,
window: &Window,
cx: &mut App,
) {
let rendered_segment = match segment {
@ -154,7 +138,6 @@ impl RenderedMessage {
text.into(),
self.language_registry.clone(),
workspace,
window,
cx,
),
scroll_handle: ScrollHandle::default(),
@ -163,7 +146,6 @@ impl RenderedMessage {
text.into(),
self.language_registry.clone(),
workspace,
window,
cx,
)),
};
@ -183,9 +165,16 @@ fn render_markdown(
text: SharedString,
language_registry: Arc<LanguageRegistry>,
workspace: WeakEntity<Workspace>,
window: &Window,
cx: &mut App,
) -> 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 colors = cx.theme().colors();
let ui_font_size = TextSize::Default.rems(cx);
@ -201,7 +190,7 @@ fn render_markdown(
..Default::default()
});
let markdown_style = MarkdownStyle {
MarkdownStyle {
base_text_style: text_style,
syntax: cx.theme().syntax().clone(),
selection_background_color: cx.theme().players().local().selection,
@ -266,24 +255,23 @@ fn render_markdown(
}
})),
..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(
text: SharedString,
language_registry: Arc<LanguageRegistry>,
workspace: WeakEntity<Workspace>,
window: &Window,
cx: &mut App,
) -> 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 colors = cx.theme().colors();
let ui_font_size = TextSize::Default.rems(cx);
@ -299,7 +287,7 @@ fn render_tool_use_markdown(
..Default::default()
});
let markdown_style = MarkdownStyle {
MarkdownStyle {
base_text_style: text_style,
syntax: cx.theme().syntax().clone(),
selection_background_color: cx.theme().players().local().selection,
@ -334,15 +322,7 @@ fn render_tool_use_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 open_markdown_link(
@ -473,7 +453,6 @@ impl ActiveThread {
tool_use.ui_text.clone(),
&tool_use.input,
tool_use.status.text(),
window,
cx,
);
}
@ -516,7 +495,7 @@ impl ActiveThread {
&mut self,
id: &MessageId,
segments: &[MessageSegment],
window: &mut Window,
_window: &mut Window,
cx: &mut Context<Self>,
) {
let old_len = self.messages.len();
@ -527,7 +506,6 @@ impl ActiveThread {
segments,
self.language_registry.clone(),
self.workspace.clone(),
window,
cx,
);
self.rendered_messages_by_id.insert(*id, rendered_message);
@ -537,7 +515,7 @@ impl ActiveThread {
&mut self,
id: &MessageId,
segments: &[MessageSegment],
window: &mut Window,
_window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(index) = self.messages.iter().position(|message_id| message_id == id) else {
@ -548,7 +526,6 @@ impl ActiveThread {
segments,
self.language_registry.clone(),
self.workspace.clone(),
window,
cx,
);
self.rendered_messages_by_id.insert(*id, rendered_message);
@ -569,7 +546,6 @@ impl ActiveThread {
tool_label: impl Into<SharedString>,
tool_input: &serde_json::Value,
tool_output: SharedString,
window: &mut Window,
cx: &mut Context<Self>,
) {
let rendered = RenderedToolUse {
@ -577,7 +553,6 @@ impl ActiveThread {
tool_label.into(),
self.language_registry.clone(),
self.workspace.clone(),
window,
cx,
),
input: render_tool_use_markdown(
@ -588,14 +563,12 @@ impl ActiveThread {
.into(),
self.language_registry.clone(),
self.workspace.clone(),
window,
cx,
),
output: render_tool_use_markdown(
tool_output,
self.language_registry.clone(),
self.workspace.clone(),
window,
cx,
),
};
@ -640,12 +613,12 @@ impl ActiveThread {
}
ThreadEvent::StreamedAssistantText(message_id, text) => {
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) => {
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) => {
@ -690,7 +663,6 @@ impl ActiveThread {
tool_use.ui_text.clone(),
&tool_use.input,
"".into(),
window,
cx,
);
}
@ -711,7 +683,6 @@ impl ActiveThread {
.tool_result(&tool_use.id)
.map(|result| result.content.clone().into())
.unwrap_or("".into()),
window,
cx,
);
}
@ -1204,6 +1175,7 @@ impl ActiveThread {
message_id,
rendered_message,
has_tool_uses,
window,
cx,
))
.into_any()
@ -1370,7 +1342,7 @@ impl ActiveThread {
div().children(
tool_uses
.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,
rendered_message: &RenderedMessage,
has_tool_uses: bool,
window: &Window,
cx: &Context<Self>,
) -> impl IntoElement {
let is_last_message = self.messages.last() == Some(&message_id);
@ -1572,12 +1545,16 @@ impl ActiveThread {
content.clone(),
&scroll_handle,
Some(index) == pending_thinking_segment_index,
window,
cx,
)
.into_any_element(),
RenderedMessageSegment::Text(markdown) => {
div().child(markdown.clone()).into_any_element()
}
RenderedMessageSegment::Text(markdown) => div()
.child(MarkdownElement::new(
markdown.clone(),
default_markdown_style(window, cx),
))
.into_any_element(),
},
),
)
@ -1601,6 +1578,7 @@ impl ActiveThread {
markdown: Entity<Markdown>,
scroll_handle: &ScrollHandle,
pending: bool,
window: &Window,
cx: &Context<Self>,
) -> impl IntoElement {
let is_open = self
@ -1734,7 +1712,10 @@ impl ActiveThread {
.h_20()
.track_scroll(scroll_handle)
.text_ui_sm(cx)
.child(markdown.clone())
.child(MarkdownElement::new(
markdown.clone(),
default_markdown_style(window, cx),
))
.overflow_hidden(),
)
.child(gradient_overlay),
@ -1749,7 +1730,10 @@ impl ActiveThread {
.rounded_b_lg()
.bg(editor_bg)
.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(
&self,
tool_use: ToolUse,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement + use<> {
let is_open = self
@ -1804,103 +1789,109 @@ impl ActiveThread {
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 = v_flex()
.gap_1()
.child(
results_content_container()
.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(
let results_content =
v_flex()
.gap_1()
.child(
results_content_container()
.border_t_1()
.border_color(self.tool_card_border_color(cx))
.child(
Label::new("Result")
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.output.clone()),
),
),
),
ToolUseStatus::Running => container.child(
results_content_container().child(
h_flex()
.gap_1()
.pb_1()
.child(div().w_full().text_ui_sm(cx).children(
rendered_tool_use.as_ref().map(|rendered| {
MarkdownElement::new(
rendered.input.clone(),
tool_use_markdown_style(window, cx),
)
}),
)),
)
.map(|container| match tool_use.status {
ToolUseStatus::Finished(_) => container.child(
results_content_container()
.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…")
Label::new("Result")
.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| {
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| {
div()
@ -1948,7 +1939,7 @@ impl ActiveThread {
)
.child(
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(
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::CodeLabel;
use markdown::Markdown;
use markdown::{Markdown, MarkdownElement};
use multi_buffer::{Anchor, ExcerptId};
use ordered_float::OrderedFloat;
use project::CompletionSource;
@ -622,21 +622,18 @@ impl CompletionsMenu {
let language = editor
.language_at(self.initial_position, cx)
.map(|l| l.name().to_proto());
Markdown::new(
SharedString::default(),
hover_markdown_style(window, cx),
languages,
language,
cx,
)
.copy_code_block_buttons(false)
.open_url(open_markdown_url)
Markdown::new(SharedString::default(), languages, language, cx)
.copy_code_block_buttons(false)
.open_url(open_markdown_url)
})
});
markdown.update(cx, |markdown, 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::SingleLine(_) => return None,

View File

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

View File

@ -14,7 +14,7 @@ use gpui::{
use itertools::Itertools;
use language::{DiagnosticEntry, Language, LanguageRegistry};
use lsp::DiagnosticSeverity;
use markdown::{Markdown, MarkdownStyle};
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use multi_buffer::{MultiOrSingleBufferOffsetRange, ToOffset};
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart};
use settings::Settings;
@ -310,7 +310,7 @@ fn show_hover(
let mut background_color: Option<Hsla> = None;
let parsed_content = cx
.new_window_entity(|window, cx| {
.new_window_entity(|_window, cx| {
let status_colors = cx.theme().status();
match local_diagnostic.diagnostic.severity {
@ -335,32 +335,8 @@ fn show_hover(
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()
});
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)
Markdown::new_text(SharedString::new(text), cx).open_url(open_markdown_url)
})
.ok();
@ -563,10 +539,9 @@ async fn parse_blocks(
.join("\n\n");
let rendered_block = cx
.new_window_entity(|window, cx| {
.new_window_entity(|_window, cx| {
Markdown::new(
combined_text.into(),
hover_markdown_style(window, cx),
Some(language_registry.clone()),
fallback_language_name,
cx,
@ -704,6 +679,7 @@ impl HoverState {
snapshot: &EditorSnapshot,
visible_rows: Range<DisplayRow>,
max_size: Size<Pixels>,
window: &mut Window,
cx: &mut Context<Editor>,
) -> Option<(DisplayPoint, Vec<AnyElement>)> {
// If there is a diagnostic, position the popovers based on that.
@ -738,10 +714,10 @@ impl HoverState {
let mut elements = Vec::new();
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 {
elements.push(info_popover.render(max_size, cx));
elements.push(info_popover.render(max_size, window, cx));
}
Some((point, elements))
@ -781,6 +757,7 @@ impl InfoPopover {
pub(crate) fn render(
&mut self,
max_size: Size<Pixels>,
window: &mut Window,
cx: &mut Context<Editor>,
) -> AnyElement {
let keyboard_grace = Rc::clone(&self.keyboard_grace);
@ -806,7 +783,10 @@ impl InfoPopover {
.max_h(max_size.height)
.p_2()
.track_scroll(&self.scroll_handle)
.child(markdown.clone()),
.child(MarkdownElement::new(
markdown.clone(),
hover_markdown_style(window, cx),
)),
)
.child(self.render_vertical_scrollbar(cx));
}
@ -868,11 +848,41 @@ pub struct 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 mut markdown_div = div().py_1().px_2();
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 {

View File

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

View File

@ -8,7 +8,7 @@ use gpui::{
App, Asset, ClipboardItem, Element, Entity, MouseButton, ParentElement, Render, ScrollHandle,
StatefulInteractiveElement, WeakEntity, prelude::*,
};
use markdown::Markdown;
use markdown::{Markdown, MarkdownElement};
use project::git_store::Repository;
use settings::Settings;
use std::hash::Hash;
@ -118,7 +118,6 @@ impl CommitTooltip {
details: Option<ParsedCommitMessage>,
repository: Entity<Repository>,
workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let commit_time = blame
@ -140,7 +139,6 @@ impl CommitTooltip {
},
repository,
workspace,
window,
cx,
)
}
@ -149,13 +147,8 @@ impl CommitTooltip {
commit: CommitDetails,
repository: Entity<Repository>,
workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut Context<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| {
Markdown::new(
commit
@ -163,7 +156,6 @@ impl CommitTooltip {
.as_ref()
.map(|message| message.message.clone())
.unwrap_or_default(),
style,
None,
None,
cx,
@ -199,12 +191,19 @@ impl Render for CommitTooltip {
OffsetDateTime::now_utc(),
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
.commit
.message
.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());
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| {
CommitTooltip::new(commit_details, repository, workspace, window, cx)
CommitTooltip::new(commit_details, repository, workspace, cx)
}));
cx.notify();
})

View File

@ -1,7 +1,7 @@
use assets::Assets;
use gpui::{Application, Entity, KeyBinding, StyleRefinement, WindowOptions, prelude::*, rgb};
use language::{LanguageRegistry, language_settings::AllLanguageSettings};
use markdown::{Markdown, MarkdownStyle};
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use node_runtime::NodeRuntime;
use settings::SettingsStore;
use std::sync::Arc;
@ -47,54 +47,7 @@ pub fn main() {
cx.activate(true);
cx.open_window(WindowOptions::default(), |_, cx| {
cx.new(|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,
)
})
cx.new(|cx| MarkdownExample::new(MARKDOWN_EXAMPLE.into(), language_registry, cx))
})
.unwrap();
});
@ -105,16 +58,10 @@ struct MarkdownExample {
}
impl MarkdownExample {
pub fn new(
text: SharedString,
style: MarkdownStyle,
language_registry: Arc<LanguageRegistry>,
cx: &mut App,
) -> Self {
pub fn new(text: SharedString, language_registry: Arc<LanguageRegistry>, cx: &mut App) -> Self {
let markdown = cx.new(|cx| {
Markdown::new(
text,
style,
Some(language_registry),
Some("TypeScript".to_string()),
cx,
@ -125,7 +72,47 @@ impl 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()
.id("markdown-example")
.debug_selector(|| "foo".into())
@ -134,6 +121,6 @@ impl Render for MarkdownExample {
.size_full()
.p_4()
.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 gpui::{Application, Entity, KeyBinding, Length, StyleRefinement, WindowOptions, rgb};
use language::{LanguageRegistry, language_settings::AllLanguageSettings};
use markdown::{Markdown, MarkdownStyle};
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use node_runtime::NodeRuntime;
use settings::SettingsStore;
use std::sync::Arc;
@ -37,58 +37,7 @@ pub fn main() {
cx.activate(true);
let _ = cx.open_window(WindowOptions::default(), |_, cx| {
cx.new(|cx| {
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()
};
let markdown = cx.new(|cx| {
Markdown::new(MARKDOWN_EXAMPLE.into(), markdown_style, None, None, cx)
});
let markdown = cx.new(|cx| Markdown::new(MARKDOWN_EXAMPLE.into(), None, None, cx));
HelloWorld { markdown }
})
@ -100,7 +49,57 @@ struct 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()
.flex()
.bg(rgb(0x2e7d32))
@ -112,6 +111,10 @@ impl Render for HelloWorld {
.border_color(rgb(0x0000ff))
.text_xl()
.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::{
AnyElement, App, BorderStyle, Bounds, ClipboardItem, CursorStyle, DispatchPhase, Edges, Entity,
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,
TextStyleRefinement, actions, point, quad,
};
@ -74,7 +74,6 @@ pub struct Markdown {
selection: Selection,
pressed_link: Option<RenderedLink>,
autoscroll_request: Option<usize>,
style: MarkdownStyle,
parsed_markdown: ParsedMarkdown,
should_reparse: bool,
pending_parse: Option<Task<Option<()>>>,
@ -97,7 +96,6 @@ actions!(markdown, [Copy]);
impl Markdown {
pub fn new(
source: SharedString,
style: MarkdownStyle,
language_registry: Option<Arc<LanguageRegistry>>,
fallback_code_block_language: Option<String>,
cx: &mut Context<Self>,
@ -108,7 +106,6 @@ impl Markdown {
selection: Selection::default(),
pressed_link: None,
autoscroll_request: None,
style,
should_reparse: false,
parsed_markdown: ParsedMarkdown::default(),
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 mut this = Self {
source,
selection: Selection::default(),
pressed_link: None,
autoscroll_request: None,
style,
should_reparse: false,
parsed_markdown: ParsedMarkdown::default(),
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 {
fn focus_handle(&self, _cx: &App) -> FocusHandle {
self.focus_handle.clone()
@ -341,7 +331,7 @@ pub struct MarkdownElement {
}
impl MarkdownElement {
fn new(markdown: Entity<Markdown>, style: MarkdownStyle) -> Self {
pub fn new(markdown: Entity<Markdown>, style: MarkdownStyle) -> Self {
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 let Some(project_path) =
window.root::<Workspace>().flatten().and_then(|workspace| {
if path_range.path.is_absolute() {
return None;
}
workspace
.read(cx)
.project()

View File

@ -13,7 +13,7 @@ use gpui::{
};
use language::CursorShape;
use markdown::{Markdown, MarkdownStyle};
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use release_channel::ReleaseChannel;
use remote::ssh_session::{ConnectionIdentifier, SshPortForwardOption};
use remote::{SshConnectionOptions, SshPlatform, SshRemoteClient};
@ -182,7 +182,6 @@ impl SshPrompt {
) {
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()),
@ -192,7 +191,6 @@ impl SshPrompt {
..Default::default()
};
text_style.refine(&refinement);
self.editor.update(cx, |editor, cx| {
if prompt.contains("yes/no") {
editor.set_masked(false, cx);
@ -202,12 +200,8 @@ impl SshPrompt {
editor.set_text_style_refinement(refinement);
editor.set_cursor_shape(CursorShape::Block, cx);
});
let markdown_style = MarkdownStyle {
base_text_style: text_style,
selection_background_color: cx.theme().players().local().selection,
..Default::default()
};
let markdown = cx.new(|cx| Markdown::new_text(prompt.into(), markdown_style, cx));
let markdown = cx.new(|cx| Markdown::new_text(prompt.into(), cx));
self.prompt = Some((markdown, tx));
self.status_message.take();
window.focus(&self.editor.focus_handle(cx));
@ -231,7 +225,26 @@ impl 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()
.key_context("PasswordPrompt")
.py_2()
@ -266,7 +279,7 @@ impl Render for SshPrompt {
div()
.size_full()
.overflow_hidden()
.child(prompt.0.clone())
.child(MarkdownElement::new(prompt.0.clone(), markdown_style))
.child(self.editor.clone()),
)
})

View File

@ -4,7 +4,7 @@ use gpui::{
Refineable, Render, RenderablePromptHandle, SharedString, Styled, TextStyleRefinement, Window,
div,
};
use markdown::{Markdown, MarkdownStyle};
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use settings::{Settings, SettingsStore};
use theme::ThemeSettings;
use ui::{
@ -47,24 +47,9 @@ fn zed_prompt_renderer(
actions: actions.iter().map(ToString::to_string).collect(),
focus: cx.focus_handle(),
active_action_id: 0,
detail: detail.filter(|text| !text.is_empty()).map(|text| {
cx.new(|cx| {
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()
});
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)
})
}),
detail: detail
.filter(|text| !text.is_empty())
.map(|text| cx.new(|cx| Markdown::new(SharedString::new(text), None, None, cx))),
}
});
@ -127,7 +112,7 @@ impl 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 font_family = settings.ui_font.family.clone();
let prompt = v_flex()
@ -153,11 +138,26 @@ impl Render for ZedPromptRenderer {
.child(self.message.clone())
.text_color(ui::Color::Default.color(cx)),
)
.children(
self.detail
.clone()
.map(|detail| div().w_full().text_xs().child(detail)),
)
.children(self.detail.clone().map(|detail| {
div()
.w_full()
.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(
self.actions.iter().enumerate().rev().map(|(ix, action)| {
ui::Button::new(ix, action.clone())