1use crate::types::TableCell;
2use gpui::{AnyElement, Entity};
3use std::ops::Range;
4use ui::{
5 ColumnWidthConfig, RedistributableColumnsState, Table, UncheckedTableRow, div, prelude::*,
6};
7
8use crate::{
9 CsvPreviewView,
10 settings::RowRenderMechanism,
11 types::{AnyColumn, DisplayCellId, DisplayRow},
12};
13
14impl CsvPreviewView {
15 /// Creates a new table.
16 /// Column number is derived from the `RedistributableColumnsState` entity.
17 pub(crate) fn create_table(
18 &self,
19 current_widths: &Entity<RedistributableColumnsState>,
20 cx: &mut Context<Self>,
21 ) -> AnyElement {
22 self.create_table_inner(self.engine.contents.rows.len(), current_widths, cx)
23 }
24
25 fn create_table_inner(
26 &self,
27 row_count: usize,
28 current_widths: &Entity<RedistributableColumnsState>,
29 cx: &mut Context<Self>,
30 ) -> AnyElement {
31 let cols = current_widths.read(cx).cols();
32 // Create headers array with interactive elements
33 let mut headers = Vec::with_capacity(cols);
34
35 headers.push(self.create_row_identifier_header(cx));
36
37 // Add the actual CSV headers with sort buttons
38 for i in 0..(cols - 1) {
39 let header_text = self
40 .engine
41 .contents
42 .headers
43 .get(AnyColumn(i))
44 .and_then(|h| h.display_value().cloned())
45 .unwrap_or_else(|| format!("Col {}", i + 1).into());
46
47 headers.push(self.create_header_element_with_sort_button(
48 header_text,
49 cx,
50 AnyColumn::from(i),
51 ));
52 }
53
54 Table::new(cols)
55 .interactable(&self.table_interaction_state)
56 .striped()
57 .width_config(ColumnWidthConfig::redistributable(current_widths.clone()))
58 .header(headers)
59 .disable_base_style()
60 .map(|table| {
61 let row_identifier_text_color = cx.theme().colors().editor_line_number;
62 match self.settings.rendering_with {
63 RowRenderMechanism::VariableList => {
64 table.variable_row_height_list(row_count, self.list_state.clone(), {
65 cx.processor(move |this, display_row: usize, _window, cx| {
66 this.performance_metrics.rendered_indices.push(display_row);
67
68 let display_row = DisplayRow(display_row);
69 Self::render_single_table_row(
70 this,
71 cols,
72 display_row,
73 row_identifier_text_color,
74 cx,
75 )
76 .unwrap_or_else(|| panic!("Expected to render a table row"))
77 })
78 })
79 }
80 RowRenderMechanism::UniformList => {
81 table.uniform_list("csv-table", row_count, {
82 cx.processor(move |this, range: Range<usize>, _window, cx| {
83 // Record all display indices in the range for performance metrics
84 this.performance_metrics
85 .rendered_indices
86 .extend(range.clone());
87
88 range
89 .filter_map(|display_index| {
90 Self::render_single_table_row(
91 this,
92 cols,
93 DisplayRow(display_index),
94 row_identifier_text_color,
95 cx,
96 )
97 })
98 .collect()
99 })
100 })
101 }
102 }
103 })
104 .into_any_element()
105 }
106
107 /// Render a single table row
108 ///
109 /// Used both by UniformList and VariableRowHeightList
110 fn render_single_table_row(
111 this: &CsvPreviewView,
112 cols: usize,
113 display_row: DisplayRow,
114 row_identifier_text_color: gpui::Hsla,
115 cx: &Context<CsvPreviewView>,
116 ) -> Option<UncheckedTableRow<AnyElement>> {
117 // Get the actual row index from our sorted indices
118 let data_row = this.engine.d2d_mapping().get_data_row(display_row)?;
119 let row = this.engine.contents.get_row(data_row)?;
120
121 let mut elements = Vec::with_capacity(cols);
122 elements.push(this.create_row_identifier_cell(display_row, data_row, cx)?);
123
124 // Remaining columns: actual CSV data
125 for col in (0..this.engine.contents.number_of_cols).map(AnyColumn) {
126 let table_cell = row.expect_get(col);
127
128 // TODO: Introduce `<null>` cell type
129 let cell_content = table_cell.display_value().cloned().unwrap_or_default();
130
131 let display_cell_id = DisplayCellId::new(display_row, col);
132
133 let cell = div().size_full().whitespace_nowrap().text_ellipsis().child(
134 CsvPreviewView::create_selectable_cell(
135 display_cell_id,
136 cell_content,
137 this.settings.vertical_alignment,
138 this.settings.font_type,
139 cx,
140 ),
141 );
142
143 elements.push(
144 div()
145 .size_full()
146 .when(this.settings.show_debug_info, |parent| {
147 parent.child(div().text_color(row_identifier_text_color).child(
148 match table_cell {
149 TableCell::Real { position: pos, .. } => {
150 let slv = pos.start.timestamp().value;
151 let so = pos.start.offset;
152 let elv = pos.end.timestamp().value;
153 let eo = pos.end.offset;
154 format!("Pos {so}(L{slv})-{eo}(L{elv})")
155 }
156 TableCell::Virtual => "Virtual cell".into(),
157 },
158 ))
159 })
160 .text_ui(cx)
161 .child(cell)
162 .into_any_element(),
163 );
164 }
165
166 Some(elements)
167 }
168}