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

Formalizing domintro typography, etc. #190

Open
domenic opened this issue Oct 8, 2020 · 9 comments
Open

Formalizing domintro typography, etc. #190

domenic opened this issue Oct 8, 2020 · 9 comments

Comments

@domenic
Copy link
Member

domenic commented Oct 8, 2020

It'd be nice to get some consistency in the "domintro" boxes for specs. By those I mean the green "For web developers (non-normative)" boxes like in:

Easy-ish issues worth thinking about:

  • Typography. I prefer making everything inside the <code> block, with nested <var>s inside for things that would be variable in JavaScript. HTML does not do this, but most other specs seem to, most of the time.

  • Spacing around method/constructor-call parentheses. HTML seems to be pretty inconsistent. Most other specs have no spaces, like idiomatic JavaScript code.

  • Spacing around .s. Everyone seems to add spaces, even though it's not idiomatic JavaScript. Probably worth keeping; it's a nice visual separator.

  • Semicolon usage. I noticed one, but probably we shouldn't.

  • Promise-returning methods. I like using await syntax with these.

  • Making sure we settle on conventions for things like constructors, (async) iterators, etc.

  • Getters. Do we do value = interface . prop? Just interface . prop? semanticName = interface . prop, like Stream's isLocked = stream . locked? This seems very inconsistent across and within specs.

  • Setters in combination with getters. We seem to do interface . prop [ = value ]. An alternative would be two <dt>s, one for the setter and one for the getter. Or just two entirely separate <dt>/<dd> pairs.

  • Varargs currently use the syntax [arguments...] (see below on the brackets). Should we align with JavaScript and use ...arguments or similar? See setTimeout/setInterval.

  • Spacing around parameter lists, optional arguments (see below), etc.

Medium-difficulty issues worth thinking about:

  • Return values from methods. Undefined-returning methods can be written simply, but should methods that return something be written with their return values shown? Probably yes.

    • But is it OK to omit the leading var/let/const? We have so far (mostly)

    • What about in cases of destructuring, e.g. { database, store, version } = storage.backingStore from KV Storage, or [branch1, branch2] = stream.tee() from Streams? Those aren't valid JS statements; you'd need to either add the leading var/let/const, or wrap the whole expression in parentheses. Maybe that's fine and we just say domintros use expression syntax?

  • When do we prefix APIs with window . or self .? And if we do, should we treat them as variables (i.e., documenting that these APIs work on any Window or any WindowOrWorkerGlobalScope)? Or should we treat them as global properties, since that's the 99% use case? E.g.:

    • HTML does window . history . length, and window . document (note the <var>s around window, indicating that it can be used with any Window instance.

    • But it does self . navigator . onLine, which is a bit strange; using self as a variable isn't something you'd expect. Maybe windowOrWorkerGlobalScope . navigator . onLine, but that's gross.

    • Most Navigator properties are prefixed with self . navigator ., but the Window-only ones are prefixed with window . navigator .. So although it might feel like the leading object is redundant, in this case it's valuable?

    • Elsewhere HTML does self . origin with no variable.

    • Elsewhere it does location . hash, which I guess is reasonable since multiple location objects are accessible. But why no leading window?

    • No other specs seem to use the leading window . or self ..

Harder issues worth thinking about:

  • Optional arguments. We use syntax like interface . method(foo [ , bar ]). This is OK-ish, but for single optional arguments (like in Streams's stream.cancel([ reason ]), it looks like we're passing an array. Can we do better?

  • "Objet literal syntax" for options arguments. This is probably best left to editor discretion, but perhaps we can come up with some guidelines. Compare the various methods Streams links above. Things to note:

    • In new ReadableStream(underlingSource[, strategy]), underlyingSource and strategy are both dictionaries. However, they are first-class concepts, so I left them as variables instead of expanding them.

    • I have two separate entries for stream.getReader() and stream.getReader({ mode: "byob" }), since they behave pretty differently, despite the IDL being optional ReadableStreamGetReaderOptions options = {}.

    • I did not add optionality brackets to any of the dictionary members in stream.pipeTo(destination[, { preventClose, preventAbort, preventCancel, signal }]), despite them all being optional. It would have gotten pretty unwieldy.

    • I did not add values for the dictionary members in pipeTo(), even though for the booleans at least, it might have been reasonable. Contrast with stream.getReader({ mode: "byob" }), where I did.

/cc @whatwg/documentation

@inexorabletash
Copy link
Member

Yes to this issue. Not sure of the best way to make progress on each line item...

Agree with the "easy" ones where @domenic expresses an opinion.

Re: getters - isLocked = stream . locked seems redundant. Vote for "no variable"

Re: omit var/let/const - yes

Re: statements vs. expressions - well, we don't require semicolons, so +1 to "say domintros use expression syntax"

Re: When do we prefix APIs with window. or self.? - with my bias acknowledged, I'd say for window-scoped APIs, window. as a prefix is helpful, but for window-or-worker then I'd leave off self. which non-spec-wonks don't write (or so I've heard). I realize it's not a boolean conditions since there are more than two types of globals, tho.

Re: options dictionaries - https://wicg.github.io/web-locks/#api-lock-manager-request is another example which calls out the options in a more documentation-like way using a nested <dl>, rather than object literal syntax. In contrast, the transaction = connection . transaction(scope [, mode [, options ] ]) example just above https://w3c.github.io/IndexedDB/#dom-idbdatabase-transaction tries to explain the options in prose, which is nasty. A convention here would be great.

Re: optional args - yes, [ , bar ] is terrible and we should do better, no great suggestions though. What do JS libraries use as documentation conventions? Maybe just listing the permutations out as "overloads" is clearest?

@annevk
Copy link
Member

annevk commented Oct 9, 2020

Bikeshed could maybe help with formatting and linking if we follow a strict template?

cc @tabatkins

@jakearchibald
Copy link

Easy-ish issues worth thinking about:

  • Typography. I prefer making everything inside the <code> block, with nested <var>s inside for things that would be variable in JavaScript. HTML does not do this, but most other specs seem to, most of the time.

Agreed. Ideally, as much as possible would be linked to their IDL types, such as the method name, the args, the return value.

  • Spacing around method/constructor-call parentheses. HTML seems to be pretty inconsistent. Most other specs have no spaces, like idiomatic JavaScript code.

I'd be happy to follow some agreed set of https://prettier.io/playground/ rules.

  • Spacing around .s. Everyone seems to add spaces, even though it's not idiomatic JavaScript. Probably worth keeping; it's a nice visual separator.

I find it confusing to read fwiw.

  • Semicolon usage. I noticed one, but probably we shouldn't.

I use them in code, but no strong feelings here. Again, happy with some Prettier rules.

  • Promise-returning methods. I like using await syntax with these.

Agreed.

  • Making sure we settle on conventions for things like constructors, (async) iterators, etc.
const thing = new Thing();
for await (const thing of iterable) { /* … */ }
  • Getters. Do we do value = interface . prop? Just interface . prop? semanticName = interface . prop, like Stream's isLocked = stream . locked? This seems very inconsistent across and within specs.

Don't think it needs a var.

  • Setters in combination with getters. We seem to do interface . prop [ = value ]. An alternative would be two <dt>s, one for the setter and one for the getter. Or just two entirely separate <dt>/<dd> pairs.

Two separate pairs I reckon.

  • Varargs currently use the syntax [arguments...] (see below on the brackets). Should we align with JavaScript and use ...arguments or similar? See setTimeout/setInterval.

Agree with ...arguments.

  • Spacing around parameter lists, optional arguments (see below), etc.

Happy with Prettier rules for spacing.

Medium-difficulty issues worth thinking about:

  • Return values from methods. Undefined-returning methods can be written simply, but should methods that return something be written with their return values shown? Probably yes.

Yeah, and the resulting var should link to the IDL type.

  • But is it OK to omit the leading var/let/const? We have so far (mostly)

I'd prefer one of var/let/const, just so it looks like good practice.

  • What about in cases of destructuring, e.g. { database, store, version } = storage.backingStore from KV Storage, or [branch1, branch2] = stream.tee() from Streams? Those aren't valid JS statements; you'd need to either add the leading var/let/const, or wrap the whole expression in parentheses. Maybe that's fine and we just say domintros use expression syntax?

Using destructuring if the return type is a dictionary seems ok. Again it'd be great if they link to their types. I agree it should have var/let/const.

  • When do we prefix APIs with window . or self .? And if we do, should we treat them as variables (i.e., documenting that these APIs work on any Window or any WindowOrWorkerGlobalScope)? Or should we treat them as global properties, since that's the 99% use case? E.g.:

    • HTML does window . history . length, and window . document (note the <var>s around window, indicating that it can be used with any Window instance.
    • But it does self . navigator . onLine, which is a bit strange; using self as a variable isn't something you'd expect. Maybe windowOrWorkerGlobalScope . navigator . onLine, but that's gross.
    • Most Navigator properties are prefixed with self . navigator ., but the Window-only ones are prefixed with window . navigator .. So although it might feel like the leading object is redundant, in this case it's valuable?
    • Elsewhere HTML does self . origin with no variable.
    • Elsewhere it does location . hash, which I guess is reasonable since multiple location objects are accessible. But why no leading window?
    • No other specs seem to use the leading window . or self ..

Fwiw, I suggested this set of rules for MDN but I don't think they did anything with it:

  1. If the item or its parent is available globally under a common name, use the name.
    • document.querySelector.
    • skipWaiting in service worker.
    • caches.match in service worker.
  2. If the item is an instance method/property, use the constructor name with the first set of caps lower-cased.
    • element.getBoundingClientRect
  3. Otherwise, just use the name.
    • FetchEvent

If something is only available in a particular context, I wonder if we can come up with a visual 'tag' of sorts.

Harder issues worth thinking about:

  • Optional arguments. We use syntax like interface . method(foo [ , bar ]). This is OK-ish, but for single optional arguments (like in Streams's stream.cancel([ reason ]), it looks like we're passing an array. Can we do better?

  • "Objet literal syntax" for options arguments. This is probably best left to editor discretion, but perhaps we can come up with some guidelines. Compare the various methods Streams links above. Things to note:

    • In new ReadableStream(underlingSource[, strategy]), underlyingSource and strategy are both dictionaries. However, they are first-class concepts, so I left them as variables instead of expanding them.
    • I have two separate entries for stream.getReader() and stream.getReader({ mode: "byob" }), since they behave pretty differently, despite the IDL being optional ReadableStreamGetReaderOptions options = {}.
    • I did not add optionality brackets to any of the dictionary members in stream.pipeTo(destination[, { preventClose, preventAbort, preventCancel, signal }]), despite them all being optional. It would have gotten pretty unwieldy.
    • I did not add values for the dictionary members in pipeTo(), even though for the booleans at least, it might have been reasonable. Contrast with stream.getReader({ mode: "byob" }), where I did.

I like foo(bar?) to indicate optional, and foo(bar = 'whatever') for optional with default value.

I like 'destructuring' for option objects too:

const result = whatever({
  foo = 'blah',
  bar?
}?);

@domenic
Copy link
Member Author

domenic commented Mar 15, 2021

Here's a halfway point that tries to decide the easy stuff. I might do a pass on HTML to try to uniformize things and gather examples to help with the harder stuff.

Decisions

  • Typography and code style:
    • Everything inside <code>
    • No spaces around parentheses
    • No spaces around .s
    • No semicolons
  • Getters/setters:
    • Separate <dt>/<dd> pairs for getters/setters. (This departs from most existing practice, but looking at a bunch of examples it will be an improvement, e.g. many sentences apply only to the setter or only to the getter. And it avoids the []-brackets.)
    • Do not have a getter return value variable
      • Exception: getters which behave the same as methods use variable return values. E.g. you do value = obj.item(x) + value = obj[x] instead of value = obj.item(x) + obj[x].
    • Default to value for the setter right-hand side but use a semantic name if you need to show multiple overloads or similar
  • Methods/constructors: no var/let/const
  • Varargs: use leading ... like JavaScript
  • Promise-returning: use await

Not yet decided

  • Optional argument syntax
  • What "base" to prefix things with
  • Destructuring for return values
  • Object literal syntax for options arguments

Canonical examples

Getter-only properties:

<dt><code><var>event</var>.<a href="...">bubbles</a></code>
<dd>Returns ... reference <var>event</var> ...

Getter/setter properties:

<dt><code><var>title</var>.<a href="...">text</a></code>
<dd>Returns ... reference <var>title</var> ...

<dt><code><var>title</var>.<a href="...">text</a> = <var>value</var></code>
<dd>... reference <var>title</var> and <var>value</var> ...

Undefined-returning methods:

<dt><code><var>table</var>.<a href="...">deleteCaption</a>()</code>
<dd>... reference <var>table</var> ...

Non-optional argument taking methods:

<dt><code><var>table</var>.<a href="...">deleteRow</a>(<var>index</var>)</code>
<dd>... reference <var>table</var> and <var>index</var> ...

Value-returning methods:

<dt><code><var>tfoot</var> = <var>table</var>.<a href="...">createTFoot</a>()</code>
<dd>... reference <var>table</var>, maybe reference <var>tfoot</var> ...

Varargs methods:

<dt><code><var>node</var>.<a href="...">append</a>(...<var>nodes</var>)</a></code>
<dd>... reference <var>node</var> and <var>nodes</var> ...</dd>

Constructors:

<dt><code><var>document</var> = new <a href="...">Document</a>()</code>
<dd>Constructs ...</dd>

Promise-for-undefined returning methods:

<dt><code>await <var>image</var>.<a href="...">decode</a>()</code>
<dd>Causes ..., returning a promise that fulfills when... The promise will be rejected when...</dd> <!-- or similar -->

Promise-for-value returning methods:

<dt><code><var>constructor</var> = await <var>customElements</var>.<a href="...">whenDefined</a>(<var>name</var>)</code>
<dd>Returns a promise that will be fulfilled with... The promise will be rejected if... reference <var>name</var>...</dd>

(note: customElements is not final and is subject to the outstanding "base" debate.)

@inexorabletash
Copy link
Member

Re: optional args

The MDN style guide on this lists out the permutations as multiple lines. There's a "Formal Syntax" section as well, but that would not be relevant for domintro. There was recent discussion leading to this.

@tabatkins
Copy link

Bikeshed could maybe help with formatting and linking if we follow a strict template?

Potentially, tho it looks like there's a pretty wide set of variations here that would make it difficult, with vars in different places and multi-level dot-access (so I can't reasonably infer the for value of later bits), plus things being set to other things...

Going thru Domenic's set of canonical examples and trying out some possible microsyntaxes, it's not gaining much, and doesn't meaningfully restrict what is put in there, either.

Tho, hm, perhaps a more expansive set of microsyntaxes (rather than trying to handle everything in one) could work...

Spaces around .s

Like @jakearchibald, I find the spaces around . to be confusing to read. It doesn't look like standard JS; of your examples in the OP, I find the ones without spaces way more readable.


The best I can see for a markup shorthand is:

Getter-only properties:

<dt><code><var>event</var> . <a href="...">bubbles</a></code>
<dt><idl getter for=Event>event.bubbles</idl>
<dd>Returns ... reference <var>event</var> ...

Getter/setter properties:

<dt><code><var>title</var> . <a href="...">text</a></code>
<dt><idl getter for=HTMLTitleElement>title.text</idl>
<dd>Returns ... reference <var>title</var> ...

<dt><code><var>title</var> . <a href="...">text</a> = <var>value</var></code>
<dt><idl setter for=HTMLTitleElement>title.text = value</idl>
<dd>... reference <var>title</var> and <var>value</var> ...

Undefined-returning methods:

<dt><code><var>table</var> . <a href="...">deleteCaption</a>()</code>
<dt><idl method for=HTMLTableElement>table.deleteCaption()</idl>
<dd>... reference <var>table</var> ...

Non-optional argument taking methods:

<dt><code><var>table</var> . <a href="...">deleteRow</a>(<var>index</var>)</code>
<dt><idl method for=HTMLTableElement>table.deleteRow(index)</idl>
<dd>... reference <var>table</var> and <var>index</var> ...

Value-returning methods:

<dt><code><var>tfoot</var> = <var>table</var> . <a href="...">createTFoot</a>()</code>
<dt><idl method for=HTMLTableElement>tfoot = table.createTFoot()</idl>
<dd>... reference <var>table</var>, maybe reference <var>tfoot</var> ...

Varargs methods:

<dt><code><var>node</var> . <a href="...">append</a>(...<var>nodes</var>)</a></code>
<dt><idl method for=Node>node.append(...nodes)</idl>
<dd>... reference <var>node</var> and <var>nodes</var> ...</dd>

Constructors:

<dt><code><var>document</var> = new <a href="...">Document</a>()</code>
<dt><idl constructor>document = new Document()</idl>
<dd>Constructs ...</dd>

Promise-for-undefined returning methods:

<dt><code>await <var>image</var> . <a href="...">decode</a>()</code>
<dt><idl promise-method for=HTMLImageElement>await image.decode()</idl>
<dd>Causes ..., returning a promise that fulfills when... The promise will be rejected when...</dd> <!-- or similar -->

Promise-for-value returning methods:

<dt><code><var>constructor</var> = await <var>customElements</var> . <a href="...">whenDefined</a>(<var>name</var>)</code>
<dt><idl promise-method for=HTMLImageElement>constructor = await customElements.whenDefined(name)</idl>
<dd>Returns a promise that will be fulfilled with... The promise will be rejected if... reference <var>name</var>...</dd>

(both of these would throw a Bikeshed error if await didn't show up in the text)

Dunno if this is worthwhile.

@annevk
Copy link
Member

annevk commented Mar 16, 2021

If Bikeshed supports <dl domintro> (translated to <dl class=domintro>) it could also insert <code> into the <dt>s and perhaps inside <dt>s it could then change {{Document}}() to <a ...>Document</a>() and not do <code> insertion and such. That would reduce clutter a lot. Or even {{Class/method(arg, arg2)}} gets turned into <a ...>method</a>(<var>arg</var>, <var>arg2</var>).

@inexorabletash
Copy link
Member

Are there any examples of domintro blocks describing EventHandlers? (I didn't spot any in a quick check.) Should we have them?

@sideshowbarker
Copy link
Contributor

Are there any examples of domintro blocks describing EventHandlers? (I didn't spot any in a quick check.) Should we have them?

We have the event-handler index at https://html.spec.whatwg.org/multipage/indices.html#ix-event-handlers

And there are a few others at places like https://html.spec.whatwg.org/multipage/media.html#cue-events in the body of the spec.

Whether documenting them that way is better or worse for developer usability than having domintro blocks for them, I dunno.

domenic added a commit to whatwg/html that referenced this issue Jul 27, 2021
This implements some of the changes described in whatwg/meta#190 (comment) for our "For web developers (non-normative)" boxes, i.e. "domintro" boxes. Although the first three domintros were rewritten by hand and thus reflect all of those changes, the rest were transformed in an automated fashion, with only the following transformations applied:

* Wrap the entire line in <code> instead of just some property names.
* Remove spaces around dots and parentheses.
* Move ...s before the argument, like JavaScript, instead of after.

Additionally the nature of the automated process involved rewrapping all the <dd> contents, and *un*wrapping all <dt> contents. Some linebreaks were removed and inserted as well to give a consistent style.
domenic added a commit to WICG/navigation-api that referenced this issue Jul 27, 2021
domenic added a commit to whatwg/websockets that referenced this issue Nov 30, 2021
* Introduces internal slots for "binary type" and "ready state"
* Uses "method steps"/"getter steps"/"setter steps" uniformly
* Updates domintro to more closely follow the conventions discussed in whatwg/meta#190 (comment)
domenic added a commit to whatwg/websockets that referenced this issue Feb 18, 2022
* Introduces internal slots for "binary type" and "ready state"
* Uses "method steps"/"getter steps"/"setter steps" uniformly
* Updates domintro to more closely follow the conventions discussed in whatwg/meta#190 (comment)
mfreed7 pushed a commit to mfreed7/html that referenced this issue Jun 3, 2022
This implements some of the changes described in whatwg/meta#190 (comment) for our "For web developers (non-normative)" boxes, i.e. "domintro" boxes. Although the first three domintros were rewritten by hand and thus reflect all of those changes, the rest were transformed in an automated fashion, with only the following transformations applied:

* Wrap the entire line in <code> instead of just some property names.
* Remove spaces around dots and parentheses.
* Move ...s before the argument, like JavaScript, instead of after.

Additionally the nature of the automated process involved rewrapping all the <dd> contents, and *un*wrapping all <dt> contents. Some linebreaks were removed and inserted as well to give a consistent style.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

7 participants
@jakearchibald @sideshowbarker @domenic @tabatkins @inexorabletash @annevk and others