mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
csv_preview: Add settings UI panel with debug tools during development (#53496)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ------ This PR adds a settings panel above the CSV table with dropdown menus to control rendering behavior, and a performance metrics overlay for debugging. Currently it's mostly used for me during dev phase, but before release of CSV preview feature all dev-only options will be cleaned up, and future features like "copy selected" will have their settings in this bar <img width="2260" height="1674" alt="image_2026-04-09_11-52-24" src="https://github.com/user-attachments/assets/9039fd59-5c46-4be4-9f33-b9825a3cdce3" /> **What changed:** - Adds `render_settings_panel()` method with dropdown menus for: - **Rendering Mode**: Variable Height (multiline support) vs Uniform Height (better performance) - **Text Alignment**: Top vs Center vertical alignment within cells - **Font Type**: UI Font vs Monospace for better readability (will be exposed to user settings) - **Experimental**: Popover menu with toggles for debug features - Adds `render_performance_metrics_overlay()` method showing: - CSV parsing duration - Rendered row count and indices - Positioned in bottom-right corner with semi-transparent styling Context: Will iterate on it with @Anthony-Eid Release Notes: - N/A --------- Co-authored-by: Anthony Eid <anthony@zed.dev>
This commit is contained in:
parent
fe9f956460
commit
ef341146d2
11 changed files with 312 additions and 59 deletions
|
|
@ -17,5 +17,8 @@ workspace.workspace = true
|
|||
log.workspace = true
|
||||
text.workspace = true
|
||||
|
||||
[features]
|
||||
dev-tools = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -179,7 +179,8 @@ impl CsvPreviewView {
|
|||
column_widths: ColumnWidths::new(cx, 1),
|
||||
parsing_task: None,
|
||||
performance_metrics: PerformanceMetrics::default(),
|
||||
list_state: gpui::ListState::new(contents.rows.len(), ListAlignment::Top, px(1.)),
|
||||
list_state: gpui::ListState::new(contents.rows.len(), ListAlignment::Top, px(1.))
|
||||
.measure_all(),
|
||||
settings: CsvPreviewSettings::default(),
|
||||
last_parse_end_time: None,
|
||||
engine: TableDataEngine::default(),
|
||||
|
|
@ -207,7 +208,8 @@ impl CsvPreviewView {
|
|||
|
||||
// Update list state with filtered row count
|
||||
let visible_rows = self.engine.d2d_mapping().visible_row_count();
|
||||
self.list_state = gpui::ListState::new(visible_rows, ListAlignment::Top, px(100.));
|
||||
self.list_state =
|
||||
gpui::ListState::new(visible_rows, ListAlignment::Top, px(100.)).measure_all();
|
||||
}
|
||||
|
||||
pub fn resolve_active_item_as_csv_editor(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
#[cfg(feature = "dev-tools")]
|
||||
mod performance_metrics_overlay;
|
||||
mod preview_view;
|
||||
mod render_table;
|
||||
mod row_identifiers;
|
||||
mod settings;
|
||||
mod table_cell;
|
||||
mod table_header;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
//! Performance metrics overlay for CSV preview debugging.
|
||||
//!
|
||||
//! Provides a semi-transparent overlay in the bottom-right corner showing
|
||||
//! CSV parsing performance metrics for developer experience.
|
||||
|
||||
use ui::{ActiveTheme, Context, IntoElement, ParentElement, Styled, StyledTypography, div};
|
||||
|
||||
use crate::{CsvPreviewView, PerformanceMetrics};
|
||||
|
||||
impl CsvPreviewView {
|
||||
/// Renders a semi-transparent performance metrics overlay in the bottom-right corner.
|
||||
///
|
||||
/// Shows CSV parsing duration for debugging and performance monitoring.
|
||||
/// The overlay is positioned absolutely and styled with reduced opacity.
|
||||
pub(crate) fn render_performance_metrics_overlay(
|
||||
&mut self,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
|
||||
let children = div()
|
||||
.absolute()
|
||||
.top_24()
|
||||
.right_4()
|
||||
.px_3()
|
||||
.py_2()
|
||||
.bg(theme.colors().editor_background)
|
||||
.border_1()
|
||||
.border_color(theme.colors().border)
|
||||
.rounded_md()
|
||||
.opacity(0.75)
|
||||
.text_xs()
|
||||
.font_buffer(cx)
|
||||
.text_color(theme.colors().text_muted)
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child("Performance metrics:")
|
||||
.children(
|
||||
format_performance_metrics(&self.performance_metrics)
|
||||
.into_iter()
|
||||
.map(|line| div().child(line)),
|
||||
);
|
||||
|
||||
// Clear rendered indices to prepare for next frame
|
||||
self.performance_metrics.rendered_indices.clear();
|
||||
children
|
||||
}
|
||||
}
|
||||
|
||||
fn format_performance_metrics(metrics: &PerformanceMetrics) -> Vec<String> {
|
||||
let mut lines = Vec::new();
|
||||
|
||||
// Add timing metrics using the display method
|
||||
let timing_display = metrics.display();
|
||||
if !timing_display.is_empty() {
|
||||
lines.extend(timing_display.lines().map(|line| format!("- {}", line)));
|
||||
} else {
|
||||
lines.push("- No timing data yet".to_string());
|
||||
}
|
||||
|
||||
// Add rendered indices information
|
||||
if metrics.rendered_indices.is_empty() {
|
||||
lines.push("- Rendered: none".to_string());
|
||||
} else {
|
||||
lines.push(format!(
|
||||
"- Rendered: {} rows",
|
||||
metrics.rendered_indices.len()
|
||||
));
|
||||
if metrics.rendered_indices.len() <= 20 {
|
||||
// Show indices if not too many
|
||||
lines.push(format!(" {:?}", metrics.rendered_indices));
|
||||
} else {
|
||||
// Show first/last few if too many
|
||||
let first_few = &metrics.rendered_indices[..5];
|
||||
let last_few = &metrics.rendered_indices[metrics.rendered_indices.len() - 5..];
|
||||
lines.push(format!(" {:?}\n..{:?}", first_few, last_few));
|
||||
}
|
||||
}
|
||||
|
||||
lines
|
||||
}
|
||||
|
|
@ -2,19 +2,19 @@ use std::time::Instant;
|
|||
|
||||
use ui::{div, prelude::*};
|
||||
|
||||
use crate::{CsvPreviewView, settings::FontType};
|
||||
use crate::CsvPreviewView;
|
||||
|
||||
impl Render for CsvPreviewView {
|
||||
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 = cx.theme();
|
||||
|
||||
self.performance_metrics.rendered_indices.clear();
|
||||
let render_prep_start = Instant::now();
|
||||
let table_with_settings = v_flex()
|
||||
.size_full()
|
||||
.p_4()
|
||||
.bg(theme.colors().editor_background)
|
||||
.track_focus(&self.focus_handle)
|
||||
.child(self.render_settings_panel(window, cx))
|
||||
.child({
|
||||
if self.engine.contents.number_of_cols == 0 {
|
||||
div()
|
||||
|
|
@ -23,10 +23,7 @@ impl Render for CsvPreviewView {
|
|||
.justify_center()
|
||||
.h_32()
|
||||
.text_ui(cx)
|
||||
.map(|div| match self.settings.font_type {
|
||||
FontType::Ui => div.font_ui(cx),
|
||||
FontType::Monospace => div.font_buffer(cx),
|
||||
})
|
||||
.font_buffer(cx)
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.child("No CSV content to display")
|
||||
.into_any_element()
|
||||
|
|
@ -41,10 +38,28 @@ impl Render for CsvPreviewView {
|
|||
(render_prep_duration, std::time::Instant::now()),
|
||||
);
|
||||
|
||||
div()
|
||||
let div = div()
|
||||
.relative()
|
||||
.w_full()
|
||||
.h_full()
|
||||
.child(table_with_settings)
|
||||
.child(table_with_settings);
|
||||
|
||||
#[cfg(feature = "dev-tools")]
|
||||
let show_perf_metrics_overlay = self.settings.show_perf_metrics_overlay;
|
||||
|
||||
#[cfg(feature = "dev-tools")]
|
||||
let div = div.when(show_perf_metrics_overlay, |div| {
|
||||
div.child(self.render_performance_metrics_overlay(cx))
|
||||
});
|
||||
|
||||
#[cfg(feature = "dev-tools")]
|
||||
if !show_perf_metrics_overlay {
|
||||
self.performance_metrics.rendered_indices.clear();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "dev-tools"))]
|
||||
self.performance_metrics.rendered_indices.clear();
|
||||
|
||||
div
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,7 +133,6 @@ impl CsvPreviewView {
|
|||
display_cell_id,
|
||||
cell_content,
|
||||
this.settings.vertical_alignment,
|
||||
this.settings.font_type,
|
||||
cx,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use ui::{
|
||||
ActiveTheme as _, AnyElement, Button, ButtonCommon as _, ButtonSize, ButtonStyle,
|
||||
Clickable as _, Context, ElementId, FluentBuilder as _, IntoElement as _, ParentElement as _,
|
||||
SharedString, Styled as _, StyledTypography as _, Tooltip, div,
|
||||
Clickable as _, Context, ElementId, IntoElement as _, ParentElement as _, SharedString,
|
||||
Styled as _, StyledTypography as _, Tooltip, div,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
CsvPreviewView,
|
||||
settings::{FontType, RowIdentifiers},
|
||||
settings::RowIdentifiers,
|
||||
types::{DataRow, DisplayRow, LineNumber},
|
||||
};
|
||||
|
||||
|
|
@ -119,10 +119,7 @@ impl CsvPreviewView {
|
|||
|
||||
let view = cx.entity();
|
||||
let value = div()
|
||||
.map(|div| match self.settings.font_type {
|
||||
FontType::Ui => div.font_ui(cx),
|
||||
FontType::Monospace => div.font_buffer(cx),
|
||||
})
|
||||
.font_buffer(cx)
|
||||
.child(
|
||||
Button::new(
|
||||
ElementId::Name("row-identifier-toggle".into()),
|
||||
|
|
@ -179,10 +176,7 @@ impl CsvPreviewView {
|
|||
// Row identifiers are always centered
|
||||
.items_center()
|
||||
.justify_end()
|
||||
.map(|div| match self.settings.font_type {
|
||||
FontType::Ui => div.font_ui(cx),
|
||||
FontType::Monospace => div.font_buffer(cx),
|
||||
})
|
||||
.font_buffer(cx)
|
||||
.child(row_identifier)
|
||||
.into_any_element();
|
||||
Some(value)
|
||||
|
|
|
|||
182
crates/csv_preview/src/renderer/settings.rs
Normal file
182
crates/csv_preview/src/renderer/settings.rs
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
use ui::{
|
||||
ActiveTheme as _, AnyElement, ButtonSize, Context, ContextMenu, DropdownMenu, ElementId,
|
||||
IntoElement as _, ParentElement as _, Styled as _, Tooltip, Window, div, h_flex,
|
||||
};
|
||||
|
||||
use crate::{CsvPreviewView, settings::VerticalAlignment};
|
||||
|
||||
///// Settings related /////
|
||||
impl CsvPreviewView {
|
||||
/// Render settings panel above the table
|
||||
pub(crate) fn render_settings_panel(
|
||||
&self,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> AnyElement {
|
||||
let current_alignment_text = match self.settings.vertical_alignment {
|
||||
VerticalAlignment::Top => "Top",
|
||||
VerticalAlignment::Center => "Center",
|
||||
};
|
||||
|
||||
let view = cx.entity();
|
||||
let alignment_dropdown_menu = ContextMenu::build(window, cx, |menu, _window, _cx| {
|
||||
menu.entry("Top", None, {
|
||||
let view = view.clone();
|
||||
move |_window, cx| {
|
||||
view.update(cx, |this, cx| {
|
||||
this.settings.vertical_alignment = VerticalAlignment::Top;
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
})
|
||||
.entry("Center", None, {
|
||||
let view = view.clone();
|
||||
move |_window, cx| {
|
||||
view.update(cx, |this, cx| {
|
||||
this.settings.vertical_alignment = VerticalAlignment::Center;
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let panel = h_flex()
|
||||
.gap_4()
|
||||
.p_2()
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.flex_wrap()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.child("Text Alignment:"),
|
||||
)
|
||||
.child(
|
||||
DropdownMenu::new(
|
||||
ElementId::Name("vertical-alignment-dropdown".into()),
|
||||
current_alignment_text,
|
||||
alignment_dropdown_menu,
|
||||
)
|
||||
.trigger_size(ButtonSize::Compact)
|
||||
.trigger_tooltip(Tooltip::text(
|
||||
"Choose vertical text alignment within cells",
|
||||
)),
|
||||
),
|
||||
);
|
||||
|
||||
#[cfg(feature = "dev-tools")]
|
||||
let panel = panel.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.child("Dev-only:"),
|
||||
)
|
||||
.child(create_dev_only_popover_menu(cx)),
|
||||
);
|
||||
|
||||
panel.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev-tools")]
|
||||
fn create_dev_only_popover_menu(
|
||||
cx: &mut Context<'_, CsvPreviewView>,
|
||||
) -> ui::PopoverMenu<ContextMenu> {
|
||||
use crate::settings::RowRenderMechanism;
|
||||
use ui::{IconButton, IconName, IconPosition, IconSize, PopoverMenu};
|
||||
|
||||
PopoverMenu::new("debug-options-menu")
|
||||
.trigger_with_tooltip(
|
||||
IconButton::new("debug-options-trigger", IconName::Settings).icon_size(IconSize::Small),
|
||||
Tooltip::text(
|
||||
"Dev-only section used for debugging purposes.\nWill be removed on public release of CSV feature"
|
||||
),
|
||||
)
|
||||
.menu({
|
||||
let view_entity = cx.entity();
|
||||
move |window, cx| {
|
||||
let view = view_entity.read(cx);
|
||||
let settings = view.settings.clone();
|
||||
Some(ContextMenu::build(window, cx, |menu, _, _| {
|
||||
menu.header("Rendering Mode")
|
||||
.toggleable_entry(
|
||||
"Variable Height",
|
||||
settings.rendering_with == RowRenderMechanism::VariableList,
|
||||
IconPosition::Start,
|
||||
None,
|
||||
{
|
||||
let view_entity = view_entity.clone();
|
||||
move |_w, cx| {
|
||||
view_entity.update(cx, |view, cx| {
|
||||
view.settings.rendering_with =
|
||||
RowRenderMechanism::VariableList;
|
||||
view.settings.multiline_cells_enabled = true;
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
.toggleable_entry(
|
||||
"Uniform Height",
|
||||
settings.rendering_with == RowRenderMechanism::UniformList,
|
||||
IconPosition::Start,
|
||||
None,
|
||||
{
|
||||
let view_entity = view_entity.clone();
|
||||
move |_w, cx| {
|
||||
view_entity.update(cx, |view, cx| {
|
||||
view.settings.rendering_with =
|
||||
RowRenderMechanism::UniformList;
|
||||
view.settings.multiline_cells_enabled = false;
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
.separator()
|
||||
.toggleable_entry(
|
||||
"Show perf metrics",
|
||||
settings.show_perf_metrics_overlay,
|
||||
IconPosition::Start,
|
||||
None,
|
||||
{
|
||||
let view_entity = view_entity.clone();
|
||||
move |_w, cx| {
|
||||
view_entity.update(cx, |view, cx| {
|
||||
view.settings.show_perf_metrics_overlay =
|
||||
!view.settings.show_perf_metrics_overlay;
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
.toggleable_entry(
|
||||
"Show cell positions",
|
||||
settings.show_debug_info,
|
||||
IconPosition::Start,
|
||||
None,
|
||||
{
|
||||
let view_entity = view_entity.clone();
|
||||
move |_, cx| {
|
||||
view_entity.update(cx, |view, cx| {
|
||||
view.settings.show_debug_info =
|
||||
!view.settings.show_debug_info;
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -3,11 +3,7 @@
|
|||
use gpui::{AnyElement, ElementId};
|
||||
use ui::{SharedString, Tooltip, div, prelude::*};
|
||||
|
||||
use crate::{
|
||||
CsvPreviewView,
|
||||
settings::{FontType, VerticalAlignment},
|
||||
types::DisplayCellId,
|
||||
};
|
||||
use crate::{CsvPreviewView, settings::VerticalAlignment, types::DisplayCellId};
|
||||
|
||||
impl CsvPreviewView {
|
||||
/// Create selectable table cell with mouse event handlers.
|
||||
|
|
@ -15,18 +11,11 @@ impl CsvPreviewView {
|
|||
display_cell_id: DisplayCellId,
|
||||
cell_content: SharedString,
|
||||
vertical_alignment: VerticalAlignment,
|
||||
font_type: FontType,
|
||||
cx: &Context<CsvPreviewView>,
|
||||
) -> AnyElement {
|
||||
create_table_cell(
|
||||
display_cell_id,
|
||||
cell_content,
|
||||
vertical_alignment,
|
||||
font_type,
|
||||
cx,
|
||||
)
|
||||
// Mouse events handlers will be here
|
||||
.into_any_element()
|
||||
create_table_cell(display_cell_id, cell_content, vertical_alignment, cx)
|
||||
// Mouse events handlers will be here
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +24,6 @@ fn create_table_cell(
|
|||
display_cell_id: DisplayCellId,
|
||||
cell_content: SharedString,
|
||||
vertical_alignment: VerticalAlignment,
|
||||
font_type: FontType,
|
||||
cx: &Context<'_, CsvPreviewView>,
|
||||
) -> gpui::Stateful<Div> {
|
||||
div()
|
||||
|
|
@ -61,10 +49,7 @@ fn create_table_cell(
|
|||
VerticalAlignment::Top => div.content_start(),
|
||||
VerticalAlignment::Center => div.content_center(),
|
||||
})
|
||||
.map(|div| match font_type {
|
||||
FontType::Ui => div.font_ui(cx),
|
||||
FontType::Monospace => div.font_buffer(cx),
|
||||
})
|
||||
.font_buffer(cx)
|
||||
.tooltip(Tooltip::text(cell_content.clone()))
|
||||
.child(div().child(cell_content))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ use ui::{Tooltip, prelude::*};
|
|||
|
||||
use crate::{
|
||||
CsvPreviewView,
|
||||
settings::FontType,
|
||||
table_data_engine::sorting_by_column::{AppliedSorting, SortDirection},
|
||||
types::AnyColumn,
|
||||
};
|
||||
|
|
@ -21,10 +20,7 @@ impl CsvPreviewView {
|
|||
.justify_between()
|
||||
.items_center()
|
||||
.w_full()
|
||||
.map(|div| match self.settings.font_type {
|
||||
FontType::Ui => div.font_ui(cx),
|
||||
FontType::Monospace => div.font_buffer(cx),
|
||||
})
|
||||
.font_buffer(cx)
|
||||
.child(div().child(header_text))
|
||||
.child(h_flex().gap_1().child(self.create_sort_button(cx, col_idx)))
|
||||
.into_any_element()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#[derive(Default, Clone, Copy)]
|
||||
#[derive(Default, Clone, Copy, PartialEq)]
|
||||
pub enum RowRenderMechanism {
|
||||
/// More correct for multiline content, but slower.
|
||||
#[allow(dead_code)] // Will be used when settings ui is added
|
||||
|
|
@ -17,15 +17,6 @@ pub enum VerticalAlignment {
|
|||
Center,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub enum FontType {
|
||||
/// Use the default UI font
|
||||
#[default]
|
||||
Ui,
|
||||
/// Use monospace font (same as buffer/editor font)
|
||||
Monospace,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub enum RowIdentifiers {
|
||||
/// Show original line numbers from CSV file
|
||||
|
|
@ -39,8 +30,9 @@ pub enum RowIdentifiers {
|
|||
pub(crate) struct CsvPreviewSettings {
|
||||
pub(crate) rendering_with: RowRenderMechanism,
|
||||
pub(crate) vertical_alignment: VerticalAlignment,
|
||||
pub(crate) font_type: FontType,
|
||||
pub(crate) numbering_type: RowIdentifiers,
|
||||
pub(crate) show_debug_info: bool,
|
||||
#[cfg(feature = "dev-tools")]
|
||||
pub(crate) show_perf_metrics_overlay: bool,
|
||||
pub(crate) multiline_cells_enabled: bool,
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue