Skip to content
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

New rendered text design for types and elements #3721

Open
srawlins opened this issue Mar 18, 2024 · 8 comments
Open

New rendered text design for types and elements #3721

srawlins opened this issue Mar 18, 2024 · 8 comments
Labels
P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug

Comments

@srawlins
Copy link
Member

There are a few issues around how we display long types and long element names. I think we can refer to these as "signatures," but it's not a super accurate name. Things like, void Function(void Fuction(int) cb, String s) and C<T extends num, S extends List<T>>. Not really signatures but not just "names."

Today these displays are hard to improve because of how we build these signatures. When we compute a function's signature as HTML, we first get the return type, and compute it's signature as HTML. Then add the name (text), then type parameters (maybe including bounds, so HTML), then parameters, so many more signatures, all as HTML. The HTML we compute are just opaque Strings. So if we wanted to compute a signature, then hard-line-break at certain points, or maybe replace long text with "..." in some places, we more-or-less can't (without re-parsing the HTML, and no thank you).

OK, so new design: instead of computing opaque Strings, we compute objects with a bit more data in them, as we go. In the simplest form, each ModelElement and ModelType has a new renderedName getter, as a sibling (and perhaps ultimate replacement) for String get linkedName. ModelTypes also need a new sibling for nameWithGenerics, something like renderedNameWithGenerics. These getters return a RenderBuffer:

class RenderBuffer {
  final StringBuffer _html;
  final StringBuffer _text;
}

The idea is to build up HTML (basically just text, span tags for styling, and a tags) in the _html field, and to also build up a String of the text that is actually displayed, in the _text field. Storing the displayed text as we build signatures allows us to:

  • compute how long a signature is, in characters, which can be used in decisions to abbreviate with "..." or to use hard line splits,
  • add "title" attributes (also good for accessibility?).
@srawlins srawlins added type-enhancement A request for a change that isn't a bug P2 A bug or feature request we're likely to work on labels Mar 18, 2024
@srawlins
Copy link
Member Author

CC @kallentu

@kallentu
Copy link
Member

compute how long a signature is, in characters, which can be used in decisions to abbreviate with "..." or to use hard line splits,

Could we perhaps just split based off of how many parameters there are? Basing it off character length might be weird for different screen sizes (if it's not dynamically determined by that).

For example, if we have 4 or more parameters, we just force the tall formatting style, each on it's own line.

@munificent
Copy link
Member

In addition, if the parameters are named, it might make sense to always split them.

@srawlins
Copy link
Member Author

Yeah I think we could. We could just pick a number, like 3 or 4, and always wrap.

We could wrap if any named parameters, like Stream.empty or Iterable.firstWhere.

@devoncarew
Copy link
Member

I think it would be worth looking at leveraging dart_style here - to feed the code fragments through dart_style and use that to manage the line-wrapping.

@srawlins
Copy link
Member Author

@devoncarew I'd love a design like that. But dart_style is not meant as an API. I think all we can get out of dart_style is an opaque String. We could try to match up elements with their new offsets in a formatted String. But this seems very tricky and error-prone with default values. Given something like:

const p2 = 'x', p3 = 'y';
const p4 = {'a': 'b'};
typedef p5 = String;
void f(List<Map<String, String>> p1 = [{}, {p2: p3, p3: p2, ...p4, "p3": "p2"}], p3, String? p2, p5? p4) {}

and the output from dart_style:

void f(
    List<Map<String, String>> p1 = [{}, {p2: p3, p3: p2, ...p4, "p3": "p2"}],
    p3,
    String p2,
    p5 p4,
) {}

it seems like a lot of details to get the new offset of "p3" and "p2" etc, in the new opaque String. We could be sneaky and do something like ask dart_style to format this:

void f(/* 1 */List</* 2 */Map</* 3 */String, /* 4 */String>> p1 = ..., p3, /* 18 */ String? p2, /* 19 */p5? p4) {}

@munificent
Copy link
Member

I think it would be worth looking at leveraging dart_style here

@kallentu and I spent some time discussing that yesterday. There is the mechanical problem @srawlins mentioned about needing to plumb offsets through so dartdoc can create links to parts within the parameters. That's solvable. The formatter can always take in an offset range and return where that range ended up to supporting preserve a selection. We could easily extend that to allow you to pass in a list of ranges.

The harder problem is that dart_style is designed to fit code within a known fixed character width assuming a monospace font. Neither of those are true for the web where the font is proportional (and user-configurable) and where we don't know how wide the page is.

I spent a little time yesterday trying to find a solution in CSS to split the parameter list if it doesn't fit but as far as I can tell it isn't possible to do so without JS.

@devoncarew
Copy link
Member

I have implemented a system like this for another tool - using dart_style to render element descriptions. The main simplifying assumption I found was that dart_style only changes the amount of white-space between tokens. So, you can write out all your tokens (remembering where they are in the string ignoring ws), dart format the string, and link elements to their locations in the new string, using the ws-ignoring indexes from before.

For my tool, I did have the benefit of always showing the code snippets in a place where I was using a mono-spaced font.

Screenshot 2024-03-19 at 8 40 17 AM Screenshot 2024-03-19 at 8 46 01 AM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests

4 participants