repl: Scale the text_style font_size and line_height (#16308)

Kyle Kelley , Mikayla , and Nate Butler created

Replaces #16273.

Release Notes:

- repl: Fixed scaling of stdout/stderr line heights

---------

Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Nate Butler <iamnbutler@gmail.com>

Change summary

crates/repl/src/outputs.rs |  4 
crates/repl/src/session.rs | 85 +++++++++++++++++++--------------------
crates/repl/src/stdio.rs   | 49 ++++++++++++++++++----
3 files changed, 84 insertions(+), 54 deletions(-)

Detailed changes

crates/repl/src/outputs.rs 🔗

@@ -250,7 +250,7 @@ pub struct ErrorView {
 }
 
 impl ErrorView {
-    fn render(&self, cx: &ViewContext<ExecutionView>) -> Option<AnyElement> {
+    fn render(&self, cx: &mut ViewContext<ExecutionView>) -> Option<AnyElement> {
         let theme = cx.theme();
 
         let padding = cx.line_height() / 2.;
@@ -358,7 +358,7 @@ pub enum OutputContent {
 }
 
 impl OutputContent {
-    fn render(&self, cx: &ViewContext<ExecutionView>) -> Option<AnyElement> {
+    fn render(&self, cx: &mut ViewContext<ExecutionView>) -> Option<AnyElement> {
         let el = match self {
             // Note: in typical frontends we would show the execute_result.execution_count
             // Here we can just handle either

crates/repl/src/session.rs 🔗

@@ -1,9 +1,9 @@
 use crate::components::KernelListItem;
-use crate::KernelStatus;
 use crate::{
     kernels::{Kernel, KernelSpecification, RunningKernel},
     outputs::{ExecutionStatus, ExecutionView},
 };
+use crate::{stdio, KernelStatus};
 use client::telemetry::Telemetry;
 use collections::{HashMap, HashSet};
 use editor::{
@@ -26,9 +26,8 @@ use runtimelib::{
     ExecuteRequest, ExecutionState, InterruptRequest, JupyterMessage, JupyterMessageContent,
     ShutdownRequest,
 };
-use settings::Settings as _;
 use std::{env::temp_dir, ops::Range, sync::Arc, time::Duration};
-use theme::{ActiveTheme, ThemeSettings};
+use theme::ActiveTheme;
 use ui::{prelude::*, IconButtonShape, Tooltip};
 
 pub struct Session {
@@ -114,68 +113,68 @@ impl EditorBlock {
     ) -> RenderBlock {
         let render = move |cx: &mut BlockContext| {
             let execution_view = execution_view.clone();
-            let text_font = ThemeSettings::get_global(cx).buffer_font.family.clone();
-            let text_font_size = ThemeSettings::get_global(cx).buffer_font_size;
+            let text_style = stdio::text_style(cx);
 
             let gutter = cx.gutter_dimensions;
-            let close_button_size = IconSize::XSmall;
 
             let block_id = cx.block_id;
             let on_close = on_close.clone();
 
             let rem_size = cx.rem_size();
-            let line_height = cx.text_style().line_height_in_pixels(rem_size);
 
-            let (close_button_width, close_button_padding) =
-                close_button_size.square_components(cx);
+            let text_line_height = text_style.line_height_in_pixels(rem_size);
+
+            let close_button = h_flex()
+                .flex_none()
+                .items_center()
+                .justify_center()
+                .absolute()
+                .top(text_line_height / 2.)
+                .right(
+                    // 2px is a magic number to nudge the button just a bit closer to
+                    // the line number start
+                    gutter.full_width() / 2.0 - text_line_height / 2.0 - px(2.),
+                )
+                .w(text_line_height)
+                .h(text_line_height)
+                .child(
+                    IconButton::new(
+                        ("close_output_area", EntityId::from(cx.block_id)),
+                        IconName::Close,
+                    )
+                    .icon_size(IconSize::Small)
+                    .icon_color(Color::Muted)
+                    .size(ButtonSize::Compact)
+                    .shape(IconButtonShape::Square)
+                    .tooltip(|cx| Tooltip::text("Close output area", cx))
+                    .on_click(move |_, cx| {
+                        if let BlockId::Custom(block_id) = block_id {
+                            (on_close)(block_id, cx)
+                        }
+                    }),
+                );
 
             div()
-                .min_h(line_height)
                 .flex()
-                .flex_row()
                 .items_start()
+                .min_h(text_line_height)
                 .w_full()
-                .bg(cx.theme().colors().background)
                 .border_y_1()
                 .border_color(cx.theme().colors().border)
+                .bg(cx.theme().colors().background)
                 .child(
-                    v_flex().min_h(cx.line_height()).justify_center().child(
-                        h_flex()
-                            .w(gutter.full_width())
-                            .justify_end()
-                            .pt(line_height / 2.)
-                            .child(
-                                h_flex()
-                                    .pr(gutter.width / 2. - close_button_width
-                                        + close_button_padding / 2.)
-                                    .child(
-                                        IconButton::new(
-                                            ("close_output_area", EntityId::from(cx.block_id)),
-                                            IconName::Close,
-                                        )
-                                        .shape(IconButtonShape::Square)
-                                        .icon_size(close_button_size)
-                                        .icon_color(Color::Muted)
-                                        .tooltip(|cx| Tooltip::text("Close output area", cx))
-                                        .on_click(
-                                            move |_, cx| {
-                                                if let BlockId::Custom(block_id) = block_id {
-                                                    (on_close)(block_id, cx)
-                                                }
-                                            },
-                                        ),
-                                    ),
-                            ),
-                    ),
+                    div()
+                        .relative()
+                        .w(gutter.full_width())
+                        .h(text_line_height * 2)
+                        .child(close_button),
                 )
                 .child(
                     div()
                         .flex_1()
                         .size_full()
-                        .my_2()
+                        .py(text_line_height / 2.)
                         .mr(gutter.width)
-                        .text_size(text_font_size)
-                        .font_family(text_font)
                         .child(execution_view),
                 )
                 .into_any_element()

crates/repl/src/stdio.rs 🔗

@@ -1,9 +1,11 @@
 use crate::outputs::ExecutionView;
 use alacritty_terminal::{term::Config, vte::ansi::Processor};
-use gpui::{canvas, size, AnyElement};
+use gpui::{canvas, size, AnyElement, FontStyle, TextStyle, WhiteSpace};
+use settings::Settings as _;
 use std::mem;
 use terminal::ZedListener;
 use terminal_view::terminal_element::TerminalElement;
+use theme::ThemeSettings;
 use ui::{prelude::*, IntoElement, ViewContext};
 
 /// Implements the most basic of terminal output for use by Jupyter outputs
@@ -22,8 +24,38 @@ pub struct TerminalOutput {
 const DEFAULT_NUM_LINES: usize = 32;
 const DEFAULT_NUM_COLUMNS: usize = 128;
 
+pub fn text_style(cx: &mut WindowContext) -> TextStyle {
+    let settings = ThemeSettings::get_global(cx).clone();
+
+    let font_family = settings.buffer_font.family;
+    let font_features = settings.buffer_font.features;
+    let font_weight = settings.buffer_font.weight;
+    let font_fallbacks = settings.buffer_font.fallbacks;
+
+    let theme = cx.theme();
+
+    let text_style = TextStyle {
+        font_family,
+        font_features,
+        font_weight,
+        font_fallbacks,
+        font_size: theme::get_buffer_font_size(cx).into(),
+        font_style: FontStyle::Normal,
+        // todo
+        line_height: cx.line_height().into(),
+        background_color: Some(theme.colors().terminal_background),
+        white_space: WhiteSpace::Normal,
+        // These are going to be overridden per-cell
+        underline: None,
+        strikethrough: None,
+        color: theme.colors().terminal_foreground,
+    };
+
+    text_style
+}
+
 pub fn terminal_size(cx: &mut WindowContext) -> terminal::TerminalSize {
-    let text_style = cx.text_style();
+    let text_style = text_style(cx);
     let text_system = cx.text_system();
 
     let line_height = cx.line_height();
@@ -86,8 +118,8 @@ impl TerminalOutput {
         }
     }
 
-    pub fn render(&self, cx: &ViewContext<ExecutionView>) -> AnyElement {
-        let text_style = cx.text_style();
+    pub fn render(&self, cx: &mut ViewContext<ExecutionView>) -> AnyElement {
+        let text_style = text_style(cx);
         let text_system = cx.text_system();
 
         let grid = self
@@ -101,10 +133,9 @@ impl TerminalOutput {
         let (cells, rects) = TerminalElement::layout_grid(grid, &text_style, text_system, None, cx);
 
         // lines are 0-indexed, so we must add 1 to get the number of lines
+        let text_line_height = text_style.line_height_in_pixels(cx.rem_size());
         let num_lines = cells.iter().map(|c| c.point.line).max().unwrap_or(0) + 1;
-        let height = num_lines as f32 * cx.line_height();
-
-        let line_height = cx.line_height();
+        let height = num_lines as f32 * text_line_height;
 
         let font_pixels = text_style.font_size.to_pixels(cx.rem_size());
         let font_id = text_system.resolve_font(&text_style.font());
@@ -124,7 +155,7 @@ impl TerminalOutput {
                         bounds.origin,
                         &terminal::TerminalSize {
                             cell_width,
-                            line_height,
+                            line_height: text_line_height,
                             size: bounds.size,
                         },
                         cx,
@@ -136,7 +167,7 @@ impl TerminalOutput {
                         bounds.origin,
                         &terminal::TerminalSize {
                             cell_width,
-                            line_height,
+                            line_height: text_line_height,
                             size: bounds.size,
                         },
                         bounds,