max / quasi
| 1 | //! Every class this renderer is responsible for, as a set rather than one name |
| 2 | //! at a time. |
| 3 | //! |
| 4 | //! The twin of [`makeover_webview::vocabulary`], one layer up, and it exists |
| 5 | //! for the same reason: a checker cannot ask "is this rule styling something |
| 6 | //! nothing emits" without the list, and this crate is one of the two places |
| 7 | //! that knows it. |
| 8 | //! |
| 9 | //! # Why an app needs this at all |
| 10 | //! |
| 11 | //! Until the described screens shipped, an app could answer the question by |
| 12 | //! reading its own markup: `makeover_build::check_vocabulary_use` took |
| 13 | //! `index.html` and the scripts beside it and asked which generated classes |
| 14 | //! appeared in none of them. goingson's swap (goingson@f4edefb) deleted all of |
| 15 | //! it, and the call was left out of that app's `build.rs` rather than run |
| 16 | //! against nothing --- an app cannot see its own document any more, because its |
| 17 | //! document is Rust in a dependency. |
| 18 | //! |
| 19 | //! That dependency is this crate. So the question moves here rather than |
| 20 | //! disappearing, which is what this module is. |
| 21 | //! |
| 22 | //! # The set is closed, and that is the useful part |
| 23 | //! |
| 24 | //! A description has no word for a class. Everything in the document comes |
| 25 | //! from this crate or from makeover. |
| 26 | //! |
| 27 | //! So [`covers`] is not a lower bound on what an app's stylesheet may |
| 28 | //! legitimately match. It is the whole of it, and a selector outside it is dead |
| 29 | //! rather than merely unaccounted for. That is what makes a drift check on the |
| 30 | //! stylesheet exact instead of advisory. |
| 31 | //! |
| 32 | //! # One family is open, and it is not ours |
| 33 | //! |
| 34 | //! `col-<name>`, from [`makeover_webview::list::push_column_class`]: a table |
| 35 | //! cell carries a class built from its column's name, which is app data. So the |
| 36 | //! set is closed over *names this tree writes* and open over *columns an app |
| 37 | //! declares*, and no list can hold the second half. |
| 38 | //! |
| 39 | //! That is why [`covers`] exists beside [`names`] and is the function to check |
| 40 | //! with. An app comparing selectors against [`names`] alone would call every |
| 41 | //! `col-` rule dead and delete the table styling. |
| 42 | //! |
| 43 | //! # What the two vocabularies hold |
| 44 | //! |
| 45 | //! ```text |
| 46 | //! makeover_webview::vocabulary::names() 73 |
| 47 | //! this crate's own names 59 |
| 48 | //! makeover names this crate spells 21 (button, list, table, run, ...) |
| 49 | //! open families 1 (col-*) |
| 50 | //! ``` |
| 51 | //! |
| 52 | //! The 21 are not repeated in [`OWN`]: this crate spells them because it emits |
| 53 | //! them, and makeover writes their rules. What [`OWN`] holds is what makeover |
| 54 | //! has never heard of --- the frame, the chrome, the region kinds, the screen |
| 55 | //! arrangements and measures, the field wrappers, the row and table |
| 56 | //! affordances --- for which the app's own stylesheet is the only thing that |
| 57 | //! can carry a rule. |
| 58 | //! |
| 59 | //! # Why this list is written down and makeover's is scraped |
| 60 | //! |
| 61 | //! makeover generates the stylesheet, so it can parse what it just wrote and |
| 62 | //! have no second source to drift from. This crate generates no CSS: it emits |
| 63 | //! markup, and there is nothing to read back. A written list is therefore the |
| 64 | //! only available shape, and [`super::tests`] carries the guard that makes it |
| 65 | //! honest --- a render of every node kind, every region kind, every notice and |
| 66 | //! every row state, scraped, with every class asserted to be covered. That |
| 67 | //! guard is not decoration: it found fourteen names a careful read of the |
| 68 | //! emitters had missed, including the `col-` family above. |
| 69 | |
| 70 | use BTreeSet; |
| 71 | |
| 72 | use Emit; |
| 73 | |
| 74 | /// The classes this crate emits that makeover does not define. |
| 75 | /// |
| 76 | /// Sorted, and kept that way: this is read as a set, and a list somebody |
| 77 | /// appends to is a list nobody can diff. |
| 78 | /// |
| 79 | /// Unprefixed. [`names`] applies the prefix, because a prefix is host |
| 80 | /// configuration and this is the vocabulary. |
| 81 | pub const OWN: & = & |
| 82 | "act-submit", |
| 83 | // The popover container an `Outcome::Anchored` lands in, emitted beside |
| 84 | // every region, beside a named control and beside a screen's selection. |
| 85 | // Not `row-menu`: both are menus and only one of them belongs to a row. |
| 86 | "anchored", |
| 87 | "ask", |
| 88 | "ask-body", |
| 89 | "ask-open", |
| 90 | // A notice's two kinds. Not makeover's: it has `well` and `card` for |
| 91 | // standing content, and no word for something the app said unprompted. |
| 92 | "banner", |
| 93 | // The region kinds, from `RegionKind`. An arrangement of regions is a |
| 94 | // page-level fact, which is why makeover has no word for one. |
| 95 | "band", |
| 96 | "bespoke", |
| 97 | "chip-remove", |
| 98 | "chrome-band", |
| 99 | "chrome-brand", |
| 100 | "chrome-brand-mark", |
| 101 | "chrome-disclose", |
| 102 | "chrome-disclose-state", |
| 103 | "chrome-nav", |
| 104 | "chrome-panel", |
| 105 | "chrome-place", |
| 106 | "chrome-search", |
| 107 | "clock", |
| 108 | "field-consults", |
| 109 | "field-suggests", |
| 110 | "field-writes", |
| 111 | // `figures`, the strip these sit in, is makeover-webview's name and is |
| 112 | // emitted here rather than owned here. It named it from 0.59.0. |
| 113 | "figure-act", |
| 114 | "form", |
| 115 | "frame", |
| 116 | "frame-status", |
| 117 | "frame-verbs", |
| 118 | "group", |
| 119 | "heading", |
| 120 | // The screen arrangements, from `Arrangement`. Two, because our apps have |
| 121 | // two; see `Webview::arrangement_class` for why a third waits for a need. |
| 122 | "list-detail", |
| 123 | "list-detail-tabbed", |
| 124 | // How wide a screen runs, from `Measure`. makeover has no word for it |
| 125 | // because the answer is a page-level arrangement rather than anything it |
| 126 | // styles. |
| 127 | "measure-contained", |
| 128 | "measure-reading", |
| 129 | "measure-wide", |
| 130 | "modal", |
| 131 | "notices", |
| 132 | "pane", |
| 133 | "region", |
| 134 | "region-consults", |
| 135 | "rest", |
| 136 | "rest-next", |
| 137 | "rest-page", |
| 138 | "rest-page-here", |
| 139 | "rest-pages", |
| 140 | "rest-position", |
| 141 | "rest-previous", |
| 142 | "rich", |
| 143 | // A list row and its two states. makeover styles the row's *parts* |
| 144 | // (`row-primary`, `row-meta`) and not the row, which is this renderer's |
| 145 | // arrangement of them. |
| 146 | "row", |
| 147 | "row-activate", |
| 148 | // The outline: a row indented under another, a row with a disclosure, and |
| 149 | // the chevron itself. `ccaa7e4b`. The indent is a `--row-depth` custom |
| 150 | // property on the row and the rule that reads it is the app's, the way |
| 151 | // `row-select`'s box is. |
| 152 | "row-branch", |
| 153 | "row-disclose", |
| 154 | "row-nested", |
| 155 | // A live selection, which is neither `row-current` nor `row-selected`. See |
| 156 | // `Row::chosen`. `1894e95d`. |
| 157 | "row-chosen", |
| 158 | "row-current", |
| 159 | "row-menu", |
| 160 | "row-select", |
| 161 | "row-selected", |
| 162 | "selector", |
| 163 | "sidebar", |
| 164 | "sidebar-content", |
| 165 | "table-row-chosen", |
| 166 | "table-row-current", |
| 167 | "table-row-menu", |
| 168 | "table-row-selected", |
| 169 | "table-disclose", |
| 170 | "table-disclose-head", |
| 171 | "table-select", |
| 172 | "table-select-head", |
| 173 | "table-sort", |
| 174 | "tabgroup", |
| 175 | "text", |
| 176 | "toast", |
| 177 | "widget", |
| 178 | ]; |
| 179 | |
| 180 | /// The prefix-free stem of the one open family. See the module header. |
| 181 | const COLUMN_STEM: &str = "col-"; |
| 182 | |
| 183 | /// Every fixed class a document served by this renderer can contain. |
| 184 | /// |
| 185 | /// [`OWN`] plus [`makeover_webview::vocabulary::names`], prefixed the way |
| 186 | /// `opts` prefixes them. The union rather than either part: this crate emits |
| 187 | /// both, and an app checking against one would call the other dead. |
| 188 | /// |
| 189 | /// A `MAKEOVER_UNLISTED` constant stood beside [`OWN`] until |
| 190 | /// makeover-webview 0.59.0, holding the three classes that crate emitted and |
| 191 | /// did not name. It names its own unruled half now, so the workaround is gone |
| 192 | /// rather than kept as a habit. |
| 193 | /// |
| 194 | /// **Prefer [`covers`] for checking a name.** This set is the fixed half; a |
| 195 | /// `col-<name>` class is legitimate and is not in here, because it cannot be. |
| 196 | /// |
| 197 | /// State classes arrive through makeover's half (`chosen`, `latched`), which |
| 198 | /// are deliberately unprefixed there: they qualify a prefixed component rather |
| 199 | /// than standing on their own. |
| 200 | |
| 201 | |
| 202 | let mut all = names; |
| 203 | all.extend; |
| 204 | all |
| 205 | |
| 206 | |
| 207 | /// Whether this class is one a document served by this renderer can contain. |
| 208 | /// |
| 209 | /// [`names`], plus the open `col-<name>` family that no set can hold. This is |
| 210 | /// the function an app's drift check should ask, and asking [`names`] instead |
| 211 | /// is how a check concludes that every table rule in a stylesheet is dead. |
| 212 | /// |
| 213 | /// # What an app does with it |
| 214 | /// |
| 215 | /// Two questions, opposite directions: |
| 216 | /// |
| 217 | /// - **Which emitted classes does the stylesheet never style?** Vocabulary that |
| 218 | /// is emitted and unstyled. Usually fine, occasionally a missing rule. |
| 219 | /// - **Which of the stylesheet's selectors match nothing that can be emitted?** |
| 220 | /// Dead CSS, and exactly dead rather than probably dead, because the set is |
| 221 | /// closed. This is the direction `check_vocabulary_use` could not answer once |
| 222 | /// the markup left the app, and it is how goingson finds out how much of a |
| 223 | /// 9,830-line stylesheet to delete. |
| 224 | |
| 225 | |
| 226 | let Some = class.strip_prefix else |
| 227 | return false; |
| 228 | ; |
| 229 | // A column class is named for app data, so it is checked by shape. The stem |
| 230 | // alone is not enough: a bare `col-` names no column, and makeover reduces |
| 231 | // a column's name to identifier characters before it becomes one. |
| 232 | if let Some = stem.strip_prefix |
| 233 | return !column.is_empty; |
| 234 | |
| 235 | names.contains |
| 236 | |
| 237 |