Repository navigation
Improve signatures in tooltips - #1336
Merged
Merged
Conversation
A snippet is a <div class="fsdocs-snippet"> holding one <pre> for the line numbers and one <pre> for the code, laid out with CSS grid instead of a table. Markdown code blocks, notebook output and non-F# snippets share the wrapper, so one selector reaches every code block on a page, the code column scrolls on its own, and the line numbers stay out of anything the reader selects and copies. A stylesheet or script that targets table.pre, td.lines, td.snippet or .code-block-wrapper needs updating. The default stylesheet ships GitHub's light and dark palettes. Tokens are grouped into roles rather than given a hue each, and every color is a --code-* custom property, so a site can restyle the scheme without touching a selector. Several token classes had no matching rule and rendered as plain text: union cases, preprocessor directives, escaped characters, enumeration cases, mutable bindings, and identifiers in every non-F# language, which is the bulk of a C#, JavaScript or HTML snippet. The stylesheet was matching u, prep, e and v, names the formatters no longer emit. It now follows Constants.CSS, and compiler errors and omitted code are styled for the first time. Tool tips carry those same classes. The compiler hands a signature over as runs it has already classified and ToolTipReader was flattening them into one string, so a tip rendered as undifferentiated text. ToolTipSpan gained a Token case for a classified run and stripParameterAttributes works on those runs instead of a string, which is a breaking change for anyone matching on ToolTipSpan. Three further defects surfaced in the tips along the way. Line breaks were lost, because the compiler puts them inside a Text run with the next line's indentation attached and only the Space case was split on, so a multi-line signature collapsed onto one line. Doc comments arrived as the raw XML they were written as, leaving summary and param tags in the tip; they are read as XML now, and a see cref keeps the name it points at. The doc comment also now reads before the signature rather than after it. Fixes a code block rendered from an OtherBlock that opened its wrapper element without ever closing it, leaving unbalanced HTML whenever snippet wrapping was on.
The compiler tags a type abbreviation `Alias` rather than tagging it as whatever it abbreviates, and ToolTipReader was grouping that with Parameter and Local under TokenKind.Identifier, which the palette renders as plain text. So `int` and `bool` were colored as types while `string`, `option`, `list`, `array` and `unit` were not, in the same position of the same signature. An alias is a type, so it now maps to ReferenceType. This is what made a tool tip read as one flat color: in the tip for CodeFormatter.ParseAndCheckSource only 5 of 34 runs carried a color, and the abbreviations account for most of the rest. UnknownEntity is the qualifier of a fully written name, the `System.` in `System.DateTime`, so it takes the module color instead of plain. Modules and namespaces move off the type color onto the green Primer reserves for tags, which was the one hue in the GitHub palette we were not using. Module access is everywhere in F# and `List.map String.trim` was a single orange run; it now alternates container and function. Type parameters stay with the types, since a leading apostrophe already marks them.
A tool tip is flowing text, so a signature wider than the tip wrapped wherever the line ran out, in the middle of a parameter as often as not. Across the documentation site 112 of the 523 code lines in tips were over 80 columns, and a type's member list was where nearly all of them sat: a `static member` with six optional parameters ran to 600 characters on one line. NiceSignaturePrint breaks such a line the way a formatter would: after the name, after each parameter of a tuple, and after each arrow, with the parameters indented under the name and the result under them. A line that fits, or that the compiler cannot read a type out of, is left as it was. The compiler's own parser decides where the breaks go. A tip line is always `<prefix> <name>: <type>`, and while `union case X.Y: ...` and a qualified `static member X.Y: ...` do not parse as they stand, the type after the first top-level colon always does. Only that part is handed to the parser, as a `val`, and the prefix and name are carried through untouched. The ranges on the resulting SynType map straight back onto the tip's classified runs, so a break only ever splits a run and every run keeps the token kind it came in with. Parsing a fragment this way costs 0.04 ms a line. A member shown indented under its type keeps that indentation, and its continuation lines indent from it rather than from the margin. The module ships with a signature file exposing `layout` alone. The fsproj lost its byte order mark when the signature was listed in it.
This was referenced Sep 23, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Before:
After: