comfy-table icon indicating copy to clipboard operation
comfy-table copied to clipboard

Enable tables to carry a title or footer

Open yoshuawuyts opened this issue 1 year ago • 10 comments

A detailed description of the feature you would like to see added.

I was looking to implement https://github.com/bytecodealliance/wasm-tools/issues/1923 using comfy-table to get a duf-like output, but it doesn't seem comfy-table is able to set titles. It would be neat if it could though.

Explain your usecase of the requested feature

When printing many tables in a row, being able to render a title adds some visual rhythm, making it easier to scan for the section you're looking for.

Alternatives

No response

Additional context

This is the output I'm trying to create:

ascii tables in a terminal

┌──────────────────────────────────────┐                                            
│ component sample-wasi-http-rust      │                                            
├──────────┬────────────────┬──────────┤                                            
│ KIND     │ PRODUCER       │ VERSION  │                                            
├──────────┼────────────────┼──────────┤                                            
│ tool     │ wit-component  │ 0.216.0  │                                            
└──────────┴────────────────┴──────────┘                                            
┌──────────────────────────────────────────────────────────────────────────────────┐
│ module sample_wasi_http_rust.wasm                                                │
├──────────┬──────────────────┬────────────────────────────────────────────────────┤
│ KIND     │ PRODUCER         │ VERSION                                            │
├──────────┼──────────────────┼────────────────────────────────────────────────────┤
│ language │ Rust             │ -                                                  │
├──────────┼──────────────────┼────────────────────────────────────────────────────┤
│ language │ C11              │ -                                                  │
├──────────┼──────────────────┼────────────────────────────────────────────────────┤
│ tool     │ rustc            │ 1.82.0 (f6e511eec 2024-10-15)                      │
├──────────┼──────────────────┼────────────────────────────────────────────────────┤
│ tool     │ clang            │ 18.1.2-wasi-sdk                                    │
│          │                  │ (https://github.com/llvm/llvm-project              │
├──────────┼──────────────────┼────────────────────────────────────────────────────┤
│ tool     │ wit-component    │ 0.20.1                                             │
├──────────┼──────────────────┼────────────────────────────────────────────────────┤
│ tool     │ wit-bindgen-rust │ 0.20.1                                             │
├──────────┼──────────────────┼────────────────────────────────────────────────────┤
│ tool     │ wit-bindgen-c    │ 0.20.1                                             │
└──────────┴──────────────────┴────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐                            
│ module wit-component:adapter:wasi_snapshot_preview1: │                            
├──────────┬──────────┬────────────────────────────────┤                            
│ KIND     │ PRODUCER │ VERSION                        │                            
├──────────┼──────────┼────────────────────────────────┤                            
│ language │ Rust     │ -                              │                            
├──────────┼──────────┼────────────────────────────────┤                            
│ tool     │ rustc    │ 1.82.0 (f6e511eec 2024-10-15)  │                            
└──────────┴──────────┴────────────────────────────────┘                            
┌────────────────────────────────────┐                                              
│ module wit-component-shim          │                                              
├──────────┬───────────────┬─────────┤                                              
│ KIND     │ PRODUCER      │ VERSION │                                              
├──────────┼───────────────┼─────────┤                                              
│ language │ Rust          │ -       │                                              
├──────────┼───────────────┼─────────┤                                              
│ tool     │ wit-component │ 0.216.0 │                                              
└──────────┴───────────────┴─────────┘                                              
┌────────────────────────────────────┐                                              
│ module wit-component:fixups        │                                              
├──────────┬───────────────┬─────────┤                                              
│ KIND     │ PRODUCER      │ VERSION │                                              
├──────────┼───────────────┼─────────┤                                              
│ language │ Rust          │ -       │                                              
├──────────┼───────────────┼─────────┤                                              
│ tool     │ wit-component │ 0.216.0 │                                              
└──────────┴───────────────┴─────────┘                                              

yoshuawuyts avatar Dec 10 '24 01:12 yoshuawuyts

I like that!

Should also be fairly easy to implement as the last step before the rendered table is returned :)

Nukesor avatar Dec 10 '24 13:12 Nukesor

A few things must be done for this to be implemented properly:

  • It must support alignment
  • Padding?
  • Same border display logic as the rest of the table
  • Styling?

Most of this logic can probably be re-used from the formatting module, the current code will need a bit of refactoring though. It's very much entangled with ColumnDisplayInfo and Cell.

Should we re-use the Cell for this and let table.set_title(title: Cell) accept a cell?

Nukesor avatar Dec 10 '24 13:12 Nukesor

Yay, I'm glad you like it!

Should we re-use the Cell for this and let table.set_title(title: Cell) accept a cell?

That seems reasonable to me. It essentially is one big cell, so that doesn't seem like a bad starting point.

Padding?

This would be nice to have, yes. At least for my use case I don't think we will immediately need it, but I could see us using this.

yoshuawuyts avatar Dec 10 '24 15:12 yoshuawuyts

If you got some spare time, feel free to start tackling this one :)

Otherwise I might get to it eventually (I hope :D), should be a fun task.

Nukesor avatar Dec 10 '24 17:12 Nukesor

I probably won't get around to taking a stab at this myself for a while, so please don't let me hold you back from implementing something fun ^^

yoshuawuyts avatar Dec 10 '24 20:12 yoshuawuyts

Turns out, I have enough to do myself :D

If anybody wants to work on this, feel free to do so :) I won't work on this for the forseeable future.

Nukesor avatar Feb 23 '25 14:02 Nukesor

So, I played around with this for a bit and turns out, this is quite a bit more tricky than expected.

The current logic for determining column sizes is column-based. Roughly explained, it works by looking at constraints and contents of the columns and trying to distribute the content in a way that the given space is used as efficiently as possible. It's a classic optimization problem where we try to find a near-perfect solution in an acceptable amount of time.

For tables that always match the full size of the terminal, adding a title is rather trivial. It gets pretty tricky though, when the title is larger than the table's content! In that case, the space distribution algorithm must be adjusted to allow setting a "overall min-width" value (the width of the title), which is then respected when the column sizes are determined. So the column sizes are no longer only depending on the columns, but also on external factors (the title). This might result in columns becoming larger than the algorithm would currently determine.

It's all doable, but the current code is pretty involved, including many loose variables. To prevent this whole thing from being even more confusing, I think we need some kind of abstraction/some kind of struct, that holds all of the layout relevant information and provides convenience helper functions with proper documentation on what the various variables mean and how they're used to determine column sizes and lateron how the table is formatted based on those values.

I already have a few ideas, but this will result in a bit of a larger refactoring, so I'm unsure when I'll get to it.

Nukesor avatar Apr 26 '25 09:04 Nukesor

@Nukesor thank you for looking into this, and thank you for the update!

yoshuawuyts avatar Apr 28 '25 13:04 yoshuawuyts

If anybody feels like tackling this issue, feel free to go ahead (just give a quick note to prevent two people working on the same thing) :D

As I sad above, this will probably need a bit of refactoring to keep the code sane. One way of keeping this clean would be to have a state struct being passed around in arrangement::dynamic::arrange, instead of having loads of individual variables. That would keep things organized and allow us to create a few helper functions for operations that manipulate multiple variables at the same time (that should then be tested).

Nukesor avatar Jul 31 '25 09:07 Nukesor

As requested by @asyd, a footer in the style of the title could also be added at the bottom of the table.

As a kind of stupid workaround:

  • Build the first table
  • Detect the width by checking the first line
  • Build another table with a single cell and that exact width
  • Style it so that it doesn't have a top/bottom border
  • Concat

Nukesor avatar Jul 31 '25 10:07 Nukesor