@cssdoc/providers 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -57,6 +57,22 @@ interface Completion {
57
57
  interface Hover {
58
58
  contents: string;
59
59
  }
60
+ /**
61
+ * How much a component hover card shows. `compact` is the header, summary, deprecation, and a facet
62
+ * count line; `full` expands every facet that has content into a labelled section; `custom` takes a
63
+ * per-section {@link HoverSections} map. A shippable stand-in for VS Code's not-yet-stable
64
+ * hover-verbosity control.
65
+ */
66
+ type HoverDetail = "compact" | "full" | "custom";
67
+ /**
68
+ * Per-section visibility for the `custom` hover detail. `auto` shows the section only when it has
69
+ * content (what `full` does for every section); `on` always shows it (empty sections render a
70
+ * placeholder); `off` hides it. Keys are the card's section names — see {@link HOVER_SECTION_KEYS}.
71
+ */
72
+ type HoverSectionMode = "on" | "off" | "auto";
73
+ type HoverSections = Record<string, HoverSectionMode>;
74
+ /** The card's section keys, in render order — the `custom` map is keyed by these. */
75
+ declare const HOVER_SECTION_KEYS: readonly ["summary", "deprecated", "remarks", "accessibility", "modifiers", "parts", "shadowParts", "states", "customProperties", "functions", "slots", "animations", "layers", "conditions", "see", "structure", "examples"];
60
76
  /** Options that tune usage checks. */
61
77
  interface UsageOptions {
62
78
  /**
@@ -131,7 +147,7 @@ declare function applyDirectives(diagnostics: Diagnostic[], source: string): Dia
131
147
  declare const record: {
132
148
  model(index: CssDocIndex, naming?: ResolvedNaming, structureIgnore?: readonly string[]): Diagnostic[];
133
149
  completions(index: CssDocIndex): Completion[];
134
- hover(base: string, index: CssDocIndex): Hover | undefined;
150
+ hover(base: string, index: CssDocIndex, detail?: HoverDetail, sections?: HoverSections): Hover | undefined;
135
151
  definition(base: string, index: CssDocIndex): Location$1 | undefined;
136
152
  };
137
153
  declare const modifier: {
@@ -185,8 +201,8 @@ declare function completeClasses(base: string | undefined, index: CssDocIndex):
185
201
  declare function completeCustomProperties(index: CssDocIndex): Completion[];
186
202
  /** Completions for a value position: the custom functions. */
187
203
  declare function completeFunctions(index: CssDocIndex): Completion[];
188
- /** Hover for a class token: the modifier's docs, else the component's. */
189
- declare function hoverForClass(base: string, token: string, index: CssDocIndex): Hover | undefined;
204
+ /** Hover for a class token: the modifier's docs, else the component's card (at the given detail). */
205
+ declare function hoverForClass(base: string, token: string, index: CssDocIndex, detail?: HoverDetail, sections?: HoverSections): Hover | undefined;
190
206
  /** Hover for a `var(--…)` custom property. */
191
207
  declare function hoverForCustomProperty(name: string, index: CssDocIndex): Hover | undefined;
192
208
  /** Hover for a custom function. */
@@ -198,4 +214,4 @@ declare function definitionForCustomProperty(name: string, index: CssDocIndex):
198
214
  /** Definition of a custom function (its `@function` rule). */
199
215
  declare function definitionForFunction(name: string, index: CssDocIndex): Location$1 | undefined;
200
216
  //#endregion
201
- export { ASPECTS, Completion, CompletionKind, DEFAULT_RULE_SEVERITIES, Diagnostic, Hover, type Location, NAME_CASE_PRESETS, NameCase, NamingRules, ResolvedNaming, RuleId, RuleSeverities, RuleSeverity, Severity, type SourceSpan, SyntaxMatch, UsageOptions, applyDirectives, checkClassUsage, checkPropertyAssignments, checkPropertyUsage, completeClasses, completeCustomProperties, completeFunctions, cssPart, customProperty, definitionForClass, definitionForCustomProperty, definitionForFunction, func, hoverForClass, hoverForCustomProperty, hoverForFunction, lintModel, matchesSyntax, modifier, parseDirectives, part, partUsage, record, resolveNaming, resolveRuleSeverities, stateUsage };
217
+ export { ASPECTS, Completion, CompletionKind, DEFAULT_RULE_SEVERITIES, Diagnostic, HOVER_SECTION_KEYS, Hover, HoverDetail, HoverSectionMode, HoverSections, type Location, NAME_CASE_PRESETS, NameCase, NamingRules, ResolvedNaming, RuleId, RuleSeverities, RuleSeverity, Severity, type SourceSpan, SyntaxMatch, UsageOptions, applyDirectives, checkClassUsage, checkPropertyAssignments, checkPropertyUsage, completeClasses, completeCustomProperties, completeFunctions, cssPart, customProperty, definitionForClass, definitionForCustomProperty, definitionForFunction, func, hoverForClass, hoverForCustomProperty, hoverForFunction, lintModel, matchesSyntax, modifier, parseDirectives, part, partUsage, record, resolveNaming, resolveRuleSeverities, stateUsage };
package/dist/index.mjs CHANGED
@@ -69,6 +69,19 @@ const globMatch = (pattern, value) => {
69
69
  if (!pattern.includes("*")) return pattern === value;
70
70
  return new RegExp(`^${pattern.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replace(/\*/gu, ".*")}$`, "u").test(value);
71
71
  };
72
+ /**
73
+ * Serialize an authored `@structure` tree back to nested CSS for a syntax-highlighted hover block. Leaf
74
+ * selectors are left bare (no `{}`) — VS Code's CSS grammar still colours them, and it reads like the
75
+ * authored `@structure` declaration; only nesting keeps braces.
76
+ */
77
+ const renderStructureTree = (nodes, depth = 0) => nodes.flatMap((n) => {
78
+ const pad = " ".repeat(depth);
79
+ return n.children.length ? [
80
+ `${pad}${n.selector} {`,
81
+ ...renderStructureTree(n.children, depth + 1),
82
+ `${pad}}`
83
+ ] : [`${pad}${n.selector}`];
84
+ });
72
85
  const record = {
73
86
  model(index, naming, structureIgnore = []) {
74
87
  const out = [];
@@ -130,23 +143,96 @@ const record = {
130
143
  deprecated: Boolean(entry.deprecated)
131
144
  }));
132
145
  },
133
- hover(base, index) {
146
+ hover(base, index, detail = "full", sections) {
134
147
  const entry = index.componentForClass(base);
135
148
  if (!entry) return void 0;
136
- const lines = [`\`${entry.className}\` ${entry.kind}`];
137
- if (entry.summary) lines.push("", entry.summary);
138
- if (entry.remarks) lines.push("", entry.remarks);
139
- const facets = [
140
- entry.modifiers.length && `${entry.modifiers.length} modifiers`,
141
- entry.parts.length && `${entry.parts.length} parts`,
142
- entry.shadowParts.length && `${entry.shadowParts.length} shadow parts`,
143
- entry.states.length && `${entry.states.length} states`,
144
- entry.cssPropertiesDeclared.length && `${entry.cssPropertiesDeclared.length} custom properties`,
145
- entry.functions.length && `${entry.functions.length} functions`,
146
- entry.conditions.length && `${entry.conditions.length} conditions`
147
- ].filter(Boolean);
148
- if (facets.length) lines.push("", facets.join(" · "));
149
- if (entry.deprecated) lines.push("", `**Deprecated** — ${entry.deprecated}`);
149
+ const selectorFor = (name) => index.matcher.selectorFor(name);
150
+ const warnHtml = (label) => `<span style="color:var(--vscode-editorWarning-foreground);">${label}</span>`;
151
+ const styled = (text, kind) => `<code style="color:var(--vscode-symbolIcon-${kind}Foreground);">${text}</code>`;
152
+ const dash = (d) => d ? ` — ${d}` : "";
153
+ const head = [`$(symbol-class) ${styled(entry.className, "class")}`, entry.kind];
154
+ if (entry.releaseStage) head.push(entry.releaseStage);
155
+ if (entry.since) head.push(`since ${entry.since}`);
156
+ const lines = [head.join(" · ")];
157
+ const deprecatedLine = entry.deprecated ? `$(warning) ${warnHtml("Deprecated")} — ${entry.deprecated}` : void 0;
158
+ if (detail === "compact") {
159
+ if (entry.summary) lines.push("", entry.summary);
160
+ if (deprecatedLine) lines.push("", deprecatedLine);
161
+ const facets = [
162
+ entry.modifiers.length && `${entry.modifiers.length} modifiers`,
163
+ entry.parts.length && `${entry.parts.length} parts`,
164
+ entry.shadowParts.length && `${entry.shadowParts.length} shadow parts`,
165
+ entry.states.length && `${entry.states.length} states`,
166
+ entry.cssPropertiesDeclared.length && `${entry.cssPropertiesDeclared.length} custom properties`,
167
+ entry.functions.length && `${entry.functions.length} functions`,
168
+ entry.conditions.length && `${entry.conditions.length} conditions`
169
+ ].filter(Boolean);
170
+ if (facets.length) lines.push("", facets.join(" · "));
171
+ return { contents: lines.join("\n") };
172
+ }
173
+ const want = (key, has) => {
174
+ const mode = detail === "custom" ? sections?.[key] ?? "auto" : "auto";
175
+ if (mode === "off") return "skip";
176
+ return has ? "content" : mode === "on" ? "empty" : "skip";
177
+ };
178
+ const prose = (key, prefix, text) => {
179
+ const w = want(key, Boolean(text?.trim()));
180
+ if (w !== "skip") lines.push("", `${prefix}${w === "content" ? text : "_—_"}`);
181
+ };
182
+ const list = (key, icon, label, rows) => {
183
+ const w = want(key, rows.length > 0);
184
+ if (w !== "skip") lines.push("", `**$(${icon}) ${label}**`, ...w === "content" ? rows : ["_—_"]);
185
+ };
186
+ prose("summary", "", entry.summary);
187
+ {
188
+ const w = want("deprecated", Boolean(entry.deprecated));
189
+ if (w !== "skip") lines.push("", w === "content" ? deprecatedLine : `$(warning) ${warnHtml("Deprecated")}`);
190
+ }
191
+ prose("remarks", "", entry.remarks);
192
+ prose("accessibility", "$(accessibility) ", entry.accessibility);
193
+ list("modifiers", "symbol-property", "Modifiers", entry.modifiers.map((m) => {
194
+ const sel = styled(selectorFor(m.name), "field");
195
+ if (m.deprecated) {
196
+ const to = m.deprecated.canonical ? ` → ${styled(selectorFor(m.deprecated.canonical), "field")}` : dash(m.deprecated.note);
197
+ return `- ${sel} — ${warnHtml("deprecated")}${to}`;
198
+ }
199
+ return `- ${sel}${dash(m.description)}`;
200
+ }));
201
+ list("parts", "symbol-field", "Parts", entry.parts.map((p) => `- ${styled(`.${p.name}`, "field")}${dash(p.description)}`));
202
+ list("shadowParts", "symbol-namespace", "Shadow parts", entry.shadowParts.map((p) => `- ${styled(`::part(${p.name})`, "field")}${dash(p.description)}`));
203
+ list("states", "symbol-event", "States", entry.states.map((s) => `- ${styled(s.kind === "custom" ? `:state(${s.name})` : `:${s.name}`, "variable")}${dash(s.description)}`));
204
+ list("customProperties", "symbol-variable", "Custom properties", entry.cssPropertiesDeclared.map((p) => {
205
+ const syntax = p.syntax ? `: \`${p.syntax}\`` : "";
206
+ const def = p.defaultValue ? ` (default \`${p.defaultValue}\`)` : "";
207
+ return `- ${styled(p.name, "variable")}${syntax}${def}${dash(p.description)}`;
208
+ }));
209
+ list("functions", "symbol-method", "Functions", entry.functions.map((f) => `- ${styled(`${f.name}()`, "method")}${dash(f.description)}`));
210
+ list("slots", "symbol-parameter", "Slots", entry.slots.map((s) => `- ${styled(s.name, "field")}${dash(s.description)}`));
211
+ list("animations", "play", "Animations", entry.animations.map((a) => `- ${styled(a.name, "method")}${dash(a.description)}`));
212
+ list("layers", "layers", "Layers", entry.layers.map((l) => `- ${styled(l.name, "class")}${dash(l.description)}`));
213
+ list("conditions", "filter", "Conditions", entry.conditions.map((c) => `- \`@${c.type} ${c.query}\`${dash(c.description)}`));
214
+ list("see", "references", "See also", entry.see.map((s) => `- ${s}`));
215
+ {
216
+ const w = want("structure", Boolean(entry.structure?.length));
217
+ if (w !== "skip") {
218
+ lines.push("", "**$(list-tree) Structure**");
219
+ if (w === "content") {
220
+ if (entry.structureDescription) lines.push("", entry.structureDescription);
221
+ lines.push("", "```css", ...renderStructureTree(entry.structure ?? []), "```");
222
+ } else lines.push("", "_—_");
223
+ }
224
+ }
225
+ {
226
+ const w = want("examples", entry.examples.length > 0);
227
+ if (w !== "skip") {
228
+ lines.push("", `**$(book) Example${entry.examples.length > 1 ? "s" : ""}**`);
229
+ if (w === "content") for (const e of entry.examples) {
230
+ const ex = e.trim();
231
+ lines.push("", ex.startsWith("```") ? ex : `\`\`\`${ex.includes("<") ? "html" : "css"}\n${ex}\n\`\`\``);
232
+ }
233
+ else lines.push("", "_—_");
234
+ }
235
+ }
150
236
  return { contents: lines.join("\n") };
151
237
  },
152
238
  definition(base, index) {
@@ -464,6 +550,26 @@ function resolveRuleSeverities(overrides) {
464
550
  }
465
551
  return resolved;
466
552
  }
553
+ /** The card's section keys, in render order — the `custom` map is keyed by these. */
554
+ const HOVER_SECTION_KEYS = [
555
+ "summary",
556
+ "deprecated",
557
+ "remarks",
558
+ "accessibility",
559
+ "modifiers",
560
+ "parts",
561
+ "shadowParts",
562
+ "states",
563
+ "customProperties",
564
+ "functions",
565
+ "slots",
566
+ "animations",
567
+ "layers",
568
+ "conditions",
569
+ "see",
570
+ "structure",
571
+ "examples"
572
+ ];
467
573
  /** The built-in name-case patterns, tested against a class name without its leading dot. */
468
574
  const NAME_CASE_PRESETS = {
469
575
  pascalCase: /^[A-Z][A-Za-z0-9]*$/u,
@@ -651,9 +757,9 @@ function completeCustomProperties(index) {
651
757
  function completeFunctions(index) {
652
758
  return func.completions(index);
653
759
  }
654
- /** Hover for a class token: the modifier's docs, else the component's. */
655
- function hoverForClass(base, token, index) {
656
- return modifier.hover(base, token, index) ?? record.hover(base, index);
760
+ /** Hover for a class token: the modifier's docs, else the component's card (at the given detail). */
761
+ function hoverForClass(base, token, index, detail = "full", sections) {
762
+ return modifier.hover(base, token, index) ?? record.hover(base, index, detail, sections);
657
763
  }
658
764
  /** Hover for a `var(--…)` custom property. */
659
765
  function hoverForCustomProperty(name, index) {
@@ -676,4 +782,4 @@ function definitionForFunction(name, index) {
676
782
  return func.definition(name, index);
677
783
  }
678
784
  //#endregion
679
- export { ASPECTS, DEFAULT_RULE_SEVERITIES, NAME_CASE_PRESETS, applyDirectives, checkClassUsage, checkPropertyAssignments, checkPropertyUsage, completeClasses, completeCustomProperties, completeFunctions, cssPart, customProperty, definitionForClass, definitionForCustomProperty, definitionForFunction, func, hoverForClass, hoverForCustomProperty, hoverForFunction, lintModel, matchesSyntax, modifier, parseDirectives, part, partUsage, record, resolveNaming, resolveRuleSeverities, stateUsage };
785
+ export { ASPECTS, DEFAULT_RULE_SEVERITIES, HOVER_SECTION_KEYS, NAME_CASE_PRESETS, applyDirectives, checkClassUsage, checkPropertyAssignments, checkPropertyUsage, completeClasses, completeCustomProperties, completeFunctions, cssPart, customProperty, definitionForClass, definitionForCustomProperty, definitionForFunction, func, hoverForClass, hoverForCustomProperty, hoverForFunction, lintModel, matchesSyntax, modifier, parseDirectives, part, partUsage, record, resolveNaming, resolveRuleSeverities, stateUsage };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cssdoc/providers",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Host-agnostic aspect providers over the @cssdoc/index — diagnostics, completions, hover, and definitions for modifiers, custom properties, structure, functions, states, and conditions.",
5
5
  "keywords": [
6
6
  "css",
@@ -29,8 +29,8 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "css-tree": "^3.2.1",
32
- "@cssdoc/core": "0.3.2",
33
- "@cssdoc/index": "0.3.2"
32
+ "@cssdoc/core": "0.4.0",
33
+ "@cssdoc/index": "0.4.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/css-tree": "^2.3.11",