-
Notifications
You must be signed in to change notification settings - Fork 651
Add arrow data tree view and syntax highlighting #10777
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 52 commits
Commits
Show all changes
85 commits
Select commit
Hold shift + click to select a range
ab24af1
Add arrow datatype inspector
lucasmerlin 6ff76ff
Claude
lucasmerlin a42a8a1
Nice, un-clauded arrow ui
lucasmerlin ef30f45
Show type info on hover and animate fade on collapse
lucasmerlin b25ddf1
Nicer type info on hover
lucasmerlin 12d9087
Pull out node =
lucasmerlin 54ce264
Basic syntax highlighting
lucasmerlin f0ff349
Show full type on hover
lucasmerlin c4907f8
Merge branch 'main' into lucas/arrow-inspector
grtlr fcc1825
Show length before lists
lucasmerlin e334e63
Add ChildNodes enum
lucasmerlin 3646adc
Fix map missing key / value
lucasmerlin b68d7a7
Implement color scheme
lucasmerlin df86cc5
Improved string format
lucasmerlin 1b5ab2d
Ranged ui
lucasmerlin 61e0d13
Ooops
lucasmerlin 697fdc4
Clippy
lucasmerlin c962c1c
Add ChildNodes::new
lucasmerlin 4ea3116
Add re_arrow_ui
lucasmerlin 9a40d9d
Create arrow_node.rs and datatype_ui.rs
lucasmerlin 41a851a
Make the MapArray work correctly
lucasmerlin b50715b
Rename to ArrayView
lucasmerlin 22af263
Inline map!
lucasmerlin 24bb2cf
Dicts work but using ArrowView in struct ChildNodes broke, I guess?
lucasmerlin 0cccc5d
Add new arrow fmt based ui
lucasmerlin 4f3cda4
Remove lots of duplicated arrow code
lucasmerlin 87de5fe
Use our custom number format
lucasmerlin 5afef3b
Remove more unused code
lucasmerlin d1f67aa
Make custom formatting actually work and ensure all datatypes are cov…
lucasmerlin 41b6e8a
Syntax highlighting!
lucasmerlin 50e75ac
Add back datatype ui
lucasmerlin 8fe195b
Remove old implementation
lucasmerlin c0820ef
Use syntax highlighting everywhere in arrow_ui
lucasmerlin b309b9b
Bring back list_item_ranges
lucasmerlin 891f985
Limit amount of formatted list items and add datatype ui at top of ar…
lucasmerlin 0d64c0b
Move ArrowNode to separate module
lucasmerlin 0ad7422
Clippy
lucasmerlin 55e077f
Update snapshots
lucasmerlin 69092c3
Undo num_instances override
lucasmerlin 9cb49bd
Merge branch 'main' into lucas/arrow-inspector
lucasmerlin 056979b
Update snapshots
lucasmerlin 87a8dfe
Fix ids changing across timesteps
lucasmerlin 81f1844
Lint fixes
lucasmerlin 773c613
Rename code_number to code_primitive
lucasmerlin b9a4292
Add docs to ARCHITECTURE.md
lucasmerlin c338fad
Match Cargo.toml with other crates
lucasmerlin 833d83a
Rename things
lucasmerlin 6bbfbea
Minor improvements
lucasmerlin 4a31dff
Rename err
lucasmerlin af1d417
More comments and inline data type
lucasmerlin 302e018
Revert snapshots
lucasmerlin e903c82
Use re_format_binary for binary data
lucasmerlin 97d5346
Add arrow testdata and snapshot tests
lucasmerlin 7fe8b32
Clippy
lucasmerlin bb0380f
Update snapshots
lucasmerlin 26973be
Merge branch 'main' into lucas/arrow-inspector
lucasmerlin cd6f301
Update architecture image
lucasmerlin f69c8d6
Fix changed version numbers
lucasmerlin 4e5acb0
Fix urls
lucasmerlin 9c10f84
Fix sort
lucasmerlin a5c8f03
Improve docs on NodeLabel::Index
lucasmerlin 0615007
Improve docs on ArrowNode::name
lucasmerlin 1189869
Remove redundant DesignTokens arg
lucasmerlin afe9777
is_item_nested doc
lucasmerlin f1d9837
Ok(())
lucasmerlin 8ad67df
Make it clear at what number arrow arrays will be limited when format…
lucasmerlin 8c5d0bc
Improve list_ui docs with an explanation of arrow offsets
lucasmerlin 9ed34a9
Improve data type ui
lucasmerlin 1629c2a
Remove old snapshot
lucasmerlin c4b156f
Update snapshots and add newline string to arrow ui test
lucasmerlin 0588afa
Rename code_name to code_identifier
lucasmerlin e89ea48
Merge branch 'main' into lucas/arrow-inspector
lucasmerlin 0afe78e
Update snapshots
lucasmerlin 323fa88
Link list_ui for an explanation of the offset indexes
lucasmerlin 19fccb8
Add separate scope for data type
lucasmerlin 08e7496
Introduce format_f16
lucasmerlin 78565f7
Clippy fixes
lucasmerlin 7eb14d9
Fix lints
lucasmerlin 23ef3eb
Fix test
lucasmerlin 4264c8e
Merge branch 'main' into lucas/arrow-inspector
lucasmerlin 152ac84
Destructure some tuples for readability
lucasmerlin 886bf93
Show nullability in arrow datatype ui
lucasmerlin e595fb8
Better import order
lucasmerlin dd3711a
Improved ShowIndex doc comment
lucasmerlin 3fcb30f
More comments
lucasmerlin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| [package] | ||
| authors.workspace = true | ||
| description = "Show arrow data in a tree of rerun list_items and format arrow with syntax highlighting." | ||
| edition.workspace = true | ||
| homepage.workspace = true | ||
| license.workspace = true | ||
| name = "re_arrow_ui" | ||
| publish = true | ||
| readme = "README.md" | ||
| repository.workspace = true | ||
| rust-version.workspace = true | ||
| version.workspace = true | ||
| include.workspace = true | ||
|
|
||
| [lints] | ||
| workspace = true | ||
|
|
||
| [dependencies] | ||
| re_arrow_util.workspace = true | ||
| re_format.workspace = true | ||
| re_tracing.workspace = true | ||
| re_ui.workspace = true | ||
|
|
||
| arrow.workspace = true | ||
| egui.workspace = true | ||
| half.workspace = true | ||
|
|
||
| [dev-dependencies] | ||
| egui_kittest.workspace = true | ||
| re_types.workspace = true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # re_arrow_ui | ||
|
|
||
| Part of the [`rerun`](https://github.com/rerun-io/rerun) family of crates. | ||
|
|
||
| [](https://crates.io/crates/re_blueprint_tree) | ||
| [](https://docs.rs/re_blueprint_tree) | ||
lucasmerlin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|  | ||
|  | ||
|
|
||
| Show arrow data in a tree of rerun list_items and format arrow with syntax highlighting. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| use crate::datatype_ui::data_type_ui; | ||
| use crate::show_index::ShowIndex; | ||
| use egui::{Id, RichText, Stroke, StrokeKind, Tooltip, Ui, WidgetText}; | ||
| use re_format::format_uint; | ||
| use re_ui::UiExt as _; | ||
| use re_ui::list_item::{LabelContent, PropertyContent, list_item_scope}; | ||
| use re_ui::syntax_highlighting::SyntaxHighlightedBuilder; | ||
|
|
||
| enum NodeLabel { | ||
| /// The index to *display*. May be different from the actual index of the value. | ||
| Index(usize), | ||
lucasmerlin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Name(String), | ||
| Custom(WidgetText), | ||
| } | ||
|
|
||
| /// Display an item of an Arrow array in a list item with some label. | ||
| pub struct ArrowNode<'a> { | ||
| label: NodeLabel, | ||
| values: &'a dyn ShowIndex, | ||
| } | ||
|
|
||
| impl<'a> ArrowNode<'a> { | ||
| /// Create a new [`ArrowNode`] with a custom label | ||
| pub fn custom(name: impl Into<WidgetText>, values: &'a dyn ShowIndex) -> Self { | ||
| Self { | ||
| label: NodeLabel::Custom(name.into()), | ||
| values, | ||
| } | ||
| } | ||
|
|
||
| /// Create a new [`ArrowNode`] with a name label. | ||
| pub fn name(name: impl Into<String>, values: &'a dyn ShowIndex) -> Self { | ||
lucasmerlin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Self { | ||
| label: NodeLabel::Name(name.into()), | ||
| values, | ||
| } | ||
| } | ||
|
|
||
| /// The index to *display* | ||
| pub fn index(idx: usize, values: &'a dyn ShowIndex) -> Self { | ||
| Self { | ||
| label: NodeLabel::Index(idx), | ||
| values, | ||
| } | ||
| } | ||
|
|
||
| /// The index of the *value* to display. | ||
| /// Can be different from [`ArrowNode::index`] e.g. in a sliced array. | ||
| pub fn show(self, ui: &mut Ui, index: usize) { | ||
| let label = match self.label { | ||
| NodeLabel::Index(idx) => { | ||
| let mut builder = SyntaxHighlightedBuilder::new(ui.style(), ui.tokens()); | ||
| builder.code_index(&format_uint(idx)); | ||
| builder.into_widget_text() | ||
| } | ||
| NodeLabel::Name(name) => { | ||
| let mut builder = SyntaxHighlightedBuilder::new(ui.style(), ui.tokens()); | ||
| builder.code_name(&name); | ||
| builder.into_widget_text() | ||
| } | ||
| NodeLabel::Custom(name) => name, | ||
| }; | ||
|
|
||
| let mut value = SyntaxHighlightedBuilder::new(ui.style(), ui.tokens()); | ||
| let result = self.values.write(index, &mut value); | ||
| let value = match result { | ||
| Ok(()) => value.into_widget_text(), | ||
| Err(err) => RichText::new(format!("Error: {err}")) | ||
| .color(ui.tokens().error_fg_color) | ||
| .into(), | ||
| }; | ||
|
|
||
| let nested = self.values.is_item_nested(); | ||
| let data_type = self.values.array().data_type(); | ||
| let data_type_ui = data_type_ui(data_type); | ||
|
|
||
| let item = ui.list_item(); | ||
| // We *don't* use index for the ID, since it might change across timesteps, | ||
| // while referring the same logical data. | ||
| let id = ui.id().with(label.text()); | ||
| let content = PropertyContent::new(label) | ||
| .value_fn(|ui, visuals| { | ||
| ui.horizontal(|ui| { | ||
| egui::Sides::new().shrink_left().show( | ||
| ui, | ||
| |ui| { | ||
| if visuals.is_collapsible() && visuals.openness() != 0.0 { | ||
| if visuals.openness() == 1.0 { | ||
| return; | ||
| } | ||
| ui.set_opacity(1.0 - visuals.openness()); | ||
| } | ||
| ui.label(value); | ||
| }, | ||
| |ui| { | ||
| let tooltip_open = | ||
| Tooltip::was_tooltip_open_last_frame(ui.ctx(), ui.next_auto_id()); | ||
| // Keep showing the data type when the tooltip is open, so the | ||
| // user can interact with it. | ||
| if visuals.hovered || tooltip_open { | ||
| let response = | ||
| ui.small(RichText::new(&data_type_ui.type_name).strong()); | ||
| ui.painter().rect_stroke( | ||
| response.rect.expand(2.0), | ||
| 4.0, | ||
| Stroke::new(1.0, visuals.text_color()), | ||
| StrokeKind::Middle, | ||
| ); | ||
|
|
||
| if let Some(content) = data_type_ui.content { | ||
| response.on_hover_ui(|ui| { | ||
| list_item_scope( | ||
| ui, | ||
| Id::new("arrow data type hover"), | ||
| |ui| { | ||
| ui.list_item().show_hierarchical_with_children( | ||
| ui, | ||
| Id::new("arrow data type item hover"), | ||
| true, | ||
| LabelContent::new(data_type_ui.type_name), | ||
| content, | ||
| ); | ||
| }, | ||
| ); | ||
| }); | ||
| } | ||
| } | ||
| }, | ||
| ); | ||
| }); | ||
| }) | ||
| .show_only_when_collapsed(false); | ||
|
|
||
| if nested { | ||
| item.show_hierarchical_with_children(ui, id, false, content, |ui| { | ||
| list_item_scope(ui, id.with("child_scope"), |ui| { | ||
| self.values.show(index, ui); | ||
| }); | ||
| }); | ||
| } else { | ||
| item.show_hierarchical(ui, content); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| use crate::datatype_ui::data_type_ui; | ||
| use crate::show_index::ArrayUi; | ||
| use arrow::{array::Array, error::ArrowError, util::display::FormatOptions}; | ||
| use egui::Id; | ||
| use re_arrow_util::ArrowArrayDowncastRef as _; | ||
| use re_ui::list_item::{PropertyContent, list_item_scope}; | ||
| use re_ui::{UiExt as _, UiLayout}; | ||
|
|
||
| pub fn arrow_ui(ui: &mut egui::Ui, ui_layout: UiLayout, array: &dyn Array) { | ||
| re_tracing::profile_function!(); | ||
|
|
||
| use arrow::array::{LargeStringArray, StringArray}; | ||
|
|
||
| ui.scope(|ui| { | ||
| ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Truncate); | ||
|
|
||
| // TODO: Should this also be handled in the arrow tree UI? | ||
| // Special-treat text. | ||
| // This is so that we can show urls as clickable links. | ||
| // Note: we match on the raw data here, so this works for any component containing text. | ||
| if let Some(entries) = array.downcast_array_ref::<StringArray>() { | ||
| if entries.len() == 1 { | ||
| let string = entries.value(0); | ||
| ui_layout.data_label(ui, string); | ||
| return; | ||
| } | ||
| } | ||
| if let Some(entries) = array.downcast_array_ref::<LargeStringArray>() { | ||
| if entries.len() == 1 { | ||
| let string = entries.value(0); | ||
| ui_layout.data_label(ui, string); | ||
| return; | ||
| } | ||
| } | ||
lucasmerlin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| match make_ui(array) { | ||
| Ok(array_formatter) => match ui_layout { | ||
| UiLayout::SelectionPanel => { | ||
| list_item_scope(ui, Id::new("arrow_ui"), |ui| { | ||
lucasmerlin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| let data_type_ui = data_type_ui(array.data_type()); | ||
| let content = PropertyContent::new("Data type") | ||
| .value_text(data_type_ui.type_name) | ||
| .show_only_when_collapsed(false); | ||
| if let Some(datatype_ui) = data_type_ui.content { | ||
| ui.list_item().show_hierarchical_with_children( | ||
| ui, | ||
| Id::new("data_type_ui_root"), | ||
| false, | ||
| content, | ||
| datatype_ui, | ||
| ); | ||
| } else { | ||
| ui.list_item().show_hierarchical(ui, content); | ||
| } | ||
| if array.len() == 1 { | ||
| array_formatter.show_value(0, ui); | ||
| } else { | ||
| array_formatter.show(ui); | ||
| } | ||
| }); | ||
| } | ||
| UiLayout::Tooltip | UiLayout::List => { | ||
| let job = if array.len() == 1 { | ||
| array_formatter.value_job(ui, 0) | ||
| } else { | ||
| array_formatter.job(ui) | ||
| }; | ||
| match job { | ||
| Ok(job) => { | ||
| ui_layout.label(ui, job); | ||
| } | ||
| Err(err) => { | ||
| ui.error_with_details_on_hover(err.to_string()); | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| Err(err) => { | ||
| ui.error_with_details_on_hover(err.to_string()); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| pub(crate) fn make_ui(array: &dyn Array) -> Result<ArrayUi<'_>, ArrowError> { | ||
| let options = FormatOptions::default() | ||
| .with_null("null") | ||
| .with_display_error(true); | ||
| let array_ui = ArrayUi::try_new(array, &options)?; | ||
| Ok(array_ui) | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.