text_view: Add to support custom code block style. (#1288)

And remove bg, padding for editor hover code blocks.

<img width="589" height="214" alt="image"
src="https://github.com/user-attachments/assets/55870f2f-e49e-4c2d-8899-9aa7edd17630"
/>
This commit is contained in:
Jason Lee 2025-09-25 17:11:14 +08:00 committed by GitHub
parent a6a14fd57b
commit 512f46aec8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 94 additions and 67 deletions

View file

@ -11,13 +11,13 @@ pub(crate) use diagnostic_popover::*;
pub(crate) use hover_popover::*; pub(crate) use hover_popover::*;
use gpui::{ use gpui::{
div, rems, App, Div, ElementId, Entity, InteractiveElement as _, IntoElement, SharedString, div, px, rems, App, Div, ElementId, Entity, InteractiveElement as _, IntoElement, SharedString,
Stateful, Styled as _, Window, Stateful, StyleRefinement, Styled as _, Window,
}; };
use crate::{ use crate::{
text::{TextView, TextViewStyle}, text::{TextView, TextViewStyle},
StyledExt as _, ActiveTheme, StyledExt as _,
}; };
pub(crate) enum ContextMenu { pub(crate) enum ContextMenu {
@ -58,7 +58,13 @@ pub(super) fn render_markdown(
1..=3 => rem_size * 1, 1..=3 => rem_size * 1,
4 => rem_size * 0.9, 4 => rem_size * 0.9,
_ => rem_size * 0.8, _ => rem_size * 0.8,
}), })
.code_block(
StyleRefinement::default()
.bg(cx.theme().transparent)
.p_0()
.text_size(px(11.)),
),
) )
.selectable() .selectable()
} }

View file

@ -1,10 +1,12 @@
mod format; mod format;
mod inline; mod inline;
mod node; mod node;
mod style;
mod text_view; mod text_view;
mod utils; mod utils;
use gpui::App; use gpui::App;
pub use style::*;
pub use text_view::*; pub use text_view::*;
pub(crate) fn init(cx: &mut App) { pub(crate) fn init(cx: &mut App) {

View file

@ -3,7 +3,7 @@ use std::{collections::HashMap, ops::Range};
use gpui::{ use gpui::{
div, img, prelude::FluentBuilder as _, px, relative, rems, AnyElement, App, DefiniteLength, div, img, prelude::FluentBuilder as _, px, relative, rems, AnyElement, App, DefiniteLength,
Div, ElementId, FontStyle, FontWeight, Half, HighlightStyle, InteractiveElement as _, Div, ElementId, FontStyle, FontWeight, Half, HighlightStyle, InteractiveElement as _,
IntoElement, Length, ObjectFit, ParentElement, Rems, SharedString, SharedUri, IntoElement, Length, ObjectFit, ParentElement, SharedString, SharedUri,
StatefulInteractiveElement, Styled, StyledImage as _, Window, StatefulInteractiveElement, Styled, StyledImage as _, Window,
}; };
use markdown::mdast; use markdown::mdast;
@ -14,7 +14,7 @@ use crate::{
highlighter::SyntaxHighlighter, highlighter::SyntaxHighlighter,
text::inline::{Inline, InlineState}, text::inline::{Inline, InlineState},
tooltip::Tooltip, tooltip::Tooltip,
v_flex, ActiveTheme as _, Icon, IconName, v_flex, ActiveTheme as _, Icon, IconName, StyledExt,
}; };
use super::{utils::list_item_prefix, TextViewStyle}; use super::{utils::list_item_prefix, TextViewStyle};
@ -318,16 +318,19 @@ impl CodeBlock {
text text
} }
fn render(&self, mb: Rems, _: &mut Window, cx: &mut App) -> AnyElement { fn render(&self, node_cx: &NodeContext, _: &mut Window, cx: &mut App) -> AnyElement {
let style = &node_cx.style;
div() div()
.id("codeblock") .id("codeblock")
.mb(mb) .mb(style.paragraph_gap)
.p_3() .p_3()
.rounded(cx.theme().radius) .rounded(cx.theme().radius)
.bg(cx.theme().accent) .bg(cx.theme().accent)
.font_family("Menlo, Monaco, Consolas, monospace") .font_family("Menlo, Monaco, Consolas, monospace")
.text_size(rems(0.875)) .text_size(rems(0.875))
.relative() .relative()
.refine_style(&style.code_block)
.child(Inline::new( .child(Inline::new(
"code", "code",
self.state.clone(), self.state.clone(),
@ -927,7 +930,7 @@ impl Node {
items items
}) })
.into_any_element(), .into_any_element(),
Node::CodeBlock(code_block) => code_block.render(mb, window, cx), Node::CodeBlock(code_block) => code_block.render(node_cx, window, cx),
Node::Table { .. } => Self::render_table(&self, node_cx, window, cx).into_any_element(), Node::Table { .. } => Self::render_table(&self, node_cx, window, cx).into_any_element(),
Node::Divider => div() Node::Divider => div()
.id("divider") .id("divider")

View file

@ -0,0 +1,67 @@
use std::{rc::Rc, sync::Arc};
use gpui::{px, rems, Pixels, Rems, StyleRefinement};
use crate::highlighter::HighlightTheme;
/// TextViewStyle used to customize the style for [`TextView`].
#[derive(Clone)]
pub struct TextViewStyle {
/// Gap of each paragraphs, default is 1 rem.
pub paragraph_gap: Rems,
/// Base font size for headings, default is 14px.
pub heading_base_font_size: Pixels,
/// Function to calculate heading font size based on heading level (1-6).
///
/// The first parameter is the heading level (1-6), the second parameter is the base font size.
/// The second parameter is the base font size.
pub heading_font_size: Option<Rc<dyn Fn(u8, Pixels) -> Pixels + 'static>>,
/// Highlight theme for code blocks. Default: [`HighlightTheme::default_light()`]
pub highlight_theme: Arc<HighlightTheme>,
/// The style refinement for code blocks.
pub code_block: StyleRefinement,
pub is_dark: bool,
}
impl PartialEq for TextViewStyle {
fn eq(&self, other: &Self) -> bool {
self.paragraph_gap == other.paragraph_gap
&& self.heading_base_font_size == other.heading_base_font_size
&& self.highlight_theme == other.highlight_theme
}
}
impl Default for TextViewStyle {
fn default() -> Self {
Self {
paragraph_gap: rems(1.),
heading_base_font_size: px(14.),
heading_font_size: None,
highlight_theme: HighlightTheme::default_light().clone(),
code_block: StyleRefinement::default(),
is_dark: false,
}
}
}
impl TextViewStyle {
/// Set paragraph gap, default is 1 rem.
pub fn paragraph_gap(mut self, gap: Rems) -> Self {
self.paragraph_gap = gap;
self
}
pub fn heading_font_size<F>(mut self, f: F) -> Self
where
F: Fn(u8, Pixels) -> Pixels + 'static,
{
self.heading_font_size = Some(Rc::new(f));
self
}
/// Set style for code blocks.
pub fn code_block(mut self, style: StyleRefinement) -> Self {
self.code_block = style;
self
}
}

View file

@ -1,18 +1,20 @@
use std::{rc::Rc, sync::Arc, time::Instant}; use std::{rc::Rc, time::Instant};
use gpui::{ use gpui::{
div, px, rems, AnyElement, App, Bounds, ClipboardItem, Element, ElementId, Entity, FocusHandle, div, AnyElement, App, Bounds, ClipboardItem, Element, ElementId, Entity, FocusHandle,
GlobalElementId, InspectorElementId, InteractiveElement, IntoElement, KeyBinding, LayoutId, GlobalElementId, InspectorElementId, InteractiveElement, IntoElement, KeyBinding, LayoutId,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Rems, RenderOnce, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, RenderOnce,
SharedString, Size, Window, SharedString, Size, Window,
}; };
use super::format::{html::HtmlElement, markdown::MarkdownElement}; use super::format::{html::HtmlElement, markdown::MarkdownElement};
use crate::{ use crate::{
global_state::GlobalState, global_state::GlobalState,
highlighter::HighlightTheme,
input::{self}, input::{self},
text::node::{self, NodeContext}, text::{
node::{self, NodeContext},
TextViewStyle,
},
}; };
const CONTEXT: &'static str = "TextView"; const CONTEXT: &'static str = "TextView";
@ -270,59 +272,6 @@ impl RenderOnce for Text {
} }
} }
/// TextViewStyle used to customize the style for [`TextView`].
#[derive(Clone)]
pub struct TextViewStyle {
/// Gap of each paragraphs, default is 1 rem.
pub paragraph_gap: Rems,
/// Base font size for headings, default is 14px.
pub heading_base_font_size: Pixels,
/// Function to calculate heading font size based on heading level (1-6).
///
/// The first parameter is the heading level (1-6), the second parameter is the base font size.
/// The second parameter is the base font size.
pub heading_font_size: Option<Rc<dyn Fn(u8, Pixels) -> Pixels + 'static>>,
/// Highlight theme for code blocks. Default: [`HighlightTheme::default_light()`]
pub highlight_theme: Arc<HighlightTheme>,
pub is_dark: bool,
}
impl PartialEq for TextViewStyle {
fn eq(&self, other: &Self) -> bool {
self.paragraph_gap == other.paragraph_gap
&& self.heading_base_font_size == other.heading_base_font_size
&& self.highlight_theme == other.highlight_theme
}
}
impl Default for TextViewStyle {
fn default() -> Self {
Self {
paragraph_gap: rems(1.),
heading_base_font_size: px(14.),
heading_font_size: None,
highlight_theme: HighlightTheme::default_light().clone(),
is_dark: false,
}
}
}
impl TextViewStyle {
/// Set paragraph gap, default is 1 rem.
pub fn paragraph_gap(mut self, gap: Rems) -> Self {
self.paragraph_gap = gap;
self
}
pub fn heading_font_size<F>(mut self, f: F) -> Self
where
F: Fn(u8, Pixels) -> Pixels + 'static,
{
self.heading_font_size = Some(Rc::new(f));
self
}
}
impl TextView { impl TextView {
/// Create a new markdown text view. /// Create a new markdown text view.
pub fn markdown( pub fn markdown(