Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 44 additions & 27 deletions docs/common/src/utils/rehype-sls-ids.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,21 @@
// comments, and whatever else grows one) free to write them unconditionally.
//
// The pages that do carry identifiers are the language specification and, in
// the safety manual, the generated SC API reference: pass
// `{ generatedReferenceRequiresIds: true }` for the latter. Those pages are
// the safety manual, everything under `reference/`: the generated SC API
// reference and the chapters the manual writes itself, like rendering and
// generated code. Pass `{ referenceRequiresIds: true }` for those. They are
// also checked for completeness -- a normative paragraph without an
// identifier fails the build -- covering top-level paragraphs of the
// specification (nested ones are asides and list items) and every paragraph
// of the generated reference. A specification chapter with `notInSC: true`
// in its frontmatter covers the full language only: the safety manual leaves
// it out, so it states no requirements and its markers are dropped like on
// any other page that carries no identifiers -- an anchor there would
// dead-link from the traceability matrix. The markers stay in the source for
// when the chapter joins the subset.
// specification and of the manual's own chapters (nested ones are asides and
// list items) and every paragraph of the generated and property-types
// reference. A page states no requirements, and so drops its markers instead
// of assigning them, in two cases: a specification chapter with
// `notInSC: true` covers the full language only, so the safety manual leaves
// it out, and a page with `normative: false` is navigational, like a section
// landing page. Dropping keeps the corpus and the traceability matrix in
// step: the matrix cites neither kind of page, and an anchor it never cites
// dead-links from nowhere and hides a marker that should have been checked.
// The markers stay in the source for when a chapter joins the subset.
//
// The same marker format lives in `split_marker` in
// docs/slint-doc-generator/traceability.rs and in the `.sls-id` styling in
Expand All @@ -51,6 +55,12 @@ const SPEC_PATH =
/[\\/]content[\\/]docs[\\/](reference[\\/])?(language|property-types)[\\/]/;
const GENERATED_REFERENCE_PATH =
/[\\/]content[\\/]docs[\\/]generated[\\/]reference[\\/]/;
// The reference chapters the safety manual writes itself. The main
// documentation serves a much larger `reference/` that states no
// requirements, so this only applies where `referenceRequiresIds` is set. The
// manual's synced property-types pages sit below this path too, but they
// match SPEC_PATH first.
const MANUAL_REFERENCE_PATH = /[\\/]content[\\/]docs[\\/]reference[\\/]/;

// A feature outside the certified subset is wrapped in the `<NotInSC>`
// component: the safety manual renders nothing for it, but rehype runs before
Expand Down Expand Up @@ -80,7 +90,7 @@ function textPreview(node) {
}

export default function rehypeSlsIds({
generatedReferenceRequiresIds = false,
referenceRequiresIds = false,
renderBadge = true,
} = {}) {
// Closure-scoped: persists across files in one build, so a duplicate
Expand All @@ -100,28 +110,35 @@ export default function rehypeSlsIds({
const isOwnPage = !siteRoot || sourcePath.startsWith(siteRoot);
const isSpec = isOwnPage && SPEC_PATH.test(sourcePath);
// Both sites generate the reference from the same doc comments, but
// only the safety manual treats it as normative.
const isNormativeReference =
generatedReferenceRequiresIds &&
isOwnPage &&
!isSpec &&
GENERATED_REFERENCE_PATH.test(sourcePath);
// only the safety manual treats it, and the reference chapters it
// writes itself, as normative.
const isReference = referenceRequiresIds && isOwnPage && !isSpec;
const isGeneratedReference =
isReference && GENERATED_REFERENCE_PATH.test(sourcePath);
const isManualReference =
isReference && MANUAL_REFERENCE_PATH.test(sourcePath);
const frontmatter = file?.data?.astro?.frontmatter;
// A `notInSC: true` chapter is outside the safety corpus, so it
// carries no identifiers and its markers are dropped.
const notInSC = isSpec && Boolean(frontmatter?.notInSC);
const assignsIds = (isSpec && !notInSC) || isNormativeReference;
// A chapter outside the SC subset, and a navigational page like a
// section landing page, both state no requirements: they carry no
// identifiers and their markers are dropped.
const statesNoRequirements =
(isSpec && Boolean(frontmatter?.notInSC)) ||
frontmatter?.normative === false;
const assignsIds =
(isSpec || isGeneratedReference || isManualReference) &&
!statesNoRequirements;
// Draft pages aren't published, so they need no ids.
const requireIds = assignsIds && !frontmatter?.draft;
// In the language specification, only top-level paragraphs are
// normative: nested ones are asides and list items. The generated SC
// reference and the property-types reference are normative at any depth
// -- the latter wraps normative prose in components like
// <SlintProperty>, so an untagged nested paragraph there is a mistake
// and fails the build rather than slipping through.
// In the language specification and the manual's own reference
// chapters, only top-level paragraphs are normative: nested ones are
// asides and list items. The generated SC reference and the
// property-types reference are normative at any depth -- both wrap
// normative prose in components like <SlintProperty>, so an untagged
// nested paragraph there is a mistake and fails the build rather than
// slipping through.
const isPropertyTypes =
isSpec && /[\\/]property-types[\\/]/.test(sourcePath);
const requiredAtAnyDepth = isNormativeReference || isPropertyTypes;
const requiredAtAnyDepth = isGeneratedReference || isPropertyTypes;

// Tracks ids claimed during *this* invocation, so re-processing the
// same file (dev-mode hot reload) re-claims its own ids cleanly while
Expand Down
4 changes: 2 additions & 2 deletions docs/safety/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default defineConfig({
// every paragraph of it carries a traceability id.
rehypePlugins: [
rehypeExternalLinksSlint,
[rehypeSlsIds, { generatedReferenceRequiresIds: true }],
[rehypeSlsIds, { referenceRequiresIds: true }],
],
},
integrations: [
Expand All @@ -52,7 +52,7 @@ export default defineConfig({
Header: "@slint/common-files/src/components/Header.astro",
Banner: "@slint/common-files/src/components/Banner.astro",
},
plugins: [slintStarlightLinksValidatorPlugin({ errorOnRelativeLinks: false })],
plugins: [slintStarlightLinksValidatorPlugin({ errorOnRelativeLinks: true })],
social: slintStarlightSocial,
sidebar: [
{ label: "Slint SC Safety Manual", slug: "index" },
Expand Down
40 changes: 36 additions & 4 deletions docs/safety/scripts/sync-language-spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@
//
// The chapters use relative links so that they resolve in both sites. Links
// that point outside the specification differ per site and are rewritten via
// LINK_MAP below.
// LINK_MAP below. The copies written here then get every link rewritten from
// the site base, because starlight-links-validator skips relative links
// entirely: a relative link would go unchecked and could rot into a dead one
// unnoticed.

import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { SAFETY_DOCS_BASE_PATH } from "../src/safety-site-config.mjs";

// The path this site is served under, with a trailing `/`.
const BASE = `/${SAFETY_DOCS_BASE_PATH.replace(/^\/+|\/+$/g, "")}/`.replace("//", "/");

// Links that leave the specification directory: canonical (docs/astro) form
// on the left, safety-manual form on the right.
Expand All @@ -27,11 +34,31 @@ function isNotInSC(content) {
// Drop `<NotInSC>` blocks entirely. The manual omits them at runtime anyway,
// but MDX evaluates a component's children eagerly, so a throwing component
// left inside one (e.g. CodeSnippetMD referencing a main-docs-only image)
// would still break the build here. Removing them leaves only the SC content.
// would still break the build here. Removing them leaves only the SC content,
// and with it only links this site can resolve: a block outside the subset
// links to chapters the manual doesn't serve, which link validation would
// reject once the links are written from the site base.
function stripNotInSC(content) {
return content.replace(/<NotInSC>[\s\S]*?<\/NotInSC>\n?/g, "");
}

// Rewrite the markdown links of a page served at `pageUrl` from the site base,
// so that starlight-links-validator checks them. Resolving against the page's
// own URL keeps the sources readable: they stay relative, and only the copies
// this script writes carry the site's layout.
function linksFromBase(content, pageUrl) {
return content.replace(/\]\((\.\.?\/[^)]*)\)/g, (_, url) => {
const resolved = new URL(url, `https://slint.dev${pageUrl}`);
return `](${resolved.pathname}${resolved.hash})`;
});
}

// URL of a synced page below `sectionUrl`; an index page heads its section.
function pageUrl(sectionUrl, entry) {
const stem = entry.replace(/\.mdx?$/, "");
return stem === "index" ? sectionUrl : `${sectionUrl}${stem}/`;
}

// Copy the files under `sourceDir` accepted by `accept` into `targetDir`,
// optionally transforming each file, writing only changed files and removing
// stale ones (minimal watcher churn).
Expand All @@ -47,7 +74,7 @@ function syncDir(sourceDir, targetDir, accept, transform = (c) => c) {
continue;
}
wanted.add(entry);
const out = transform(content);
const out = transform(content, entry);
const targetFile = join(targetDir, entry);
if (!existsSync(targetFile) || readFileSync(targetFile, "utf-8") !== out) {
writeFileSync(targetFile, out);
Expand Down Expand Up @@ -82,6 +109,7 @@ for (const entry of readdirSync(source)) {
for (const [from, to] of LINK_MAP) {
content = content.replaceAll(from, to);
}
content = linksFromBase(stripNotInSC(content), pageUrl(`${BASE}language/`, entry));
const targetFile = join(target, entry);
if (!existsSync(targetFile) || readFileSync(targetFile, "utf-8") !== content) {
writeFileSync(targetFile, content);
Expand All @@ -101,7 +129,11 @@ syncDir(
join(here, "../../astro/src/content/docs/reference/property-types"),
join(here, "../src/content/docs/reference/property-types"),
(content) => !isNotInSC(content),
stripNotInSC,
(content, entry) =>
linksFromBase(
stripNotInSC(content),
pageUrl(`${BASE}reference/property-types/`, entry),
),
);

console.log(`Synced language specification from ${source}`);
16 changes: 14 additions & 2 deletions docs/safety/src/content.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT
import { defineCollection } from "astro:content";
import { defineCollection, z } from "astro:content";
import { docsLoader } from "@astrojs/starlight/loaders";
import { docsSchema } from "@astrojs/starlight/schema";

export const collections = {
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
docs: defineCollection({
loader: docsLoader(),
schema: docsSchema({
extend: z.object({
// Navigational page, like a section landing page: it states
// no requirements, so its paragraphs carry no `{#sls.…}`
// identifiers. Defaults to true for every other page under
// `reference/`, which the completeness check in
// rehype-sls-ids.mjs holds to the corpus rules.
normative: z.boolean().optional(),
}),
}),
}),
};
2 changes: 1 addition & 1 deletion docs/safety/src/content/docs/development-phases.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ For external contributions, the reviewer must merge the PR.

We have a set of tests that are executed as part of this phase.
These tests can be run locally using the "cargo test" command.
They are described in more detail here: [Tests](../qualification-plan/test-cases/).
They are described in more detail here: [Tests](/qualification-plan/test-cases/).



Expand Down
6 changes: 3 additions & 3 deletions docs/safety/src/content/docs/reference/generated-code.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: The Rust API that the Slint SC compiler generates.
---

The Slint SC compiler translates a `.slint` file into Rust code that depends
only on the `slint-sc` runtime crate.
only on the `slint-sc` runtime crate. \{#sls.gen.output}

## The Component Struct

Expand All @@ -28,7 +28,7 @@ pub fn render_rgb8(&self, width: u32, height: u32, frame_buffer: &mut [u8]) -> R
```

Renders the window into `frame_buffer` as described in
[Rendering](../rendering/):
[Rendering](/reference/rendering/):
`width * height` pixels in row-major order, each pixel three bytes — red,
green, blue. \{#sls.gen.render-rgb8}

Expand All @@ -44,7 +44,7 @@ returns `Err(RenderError::InvalidFrameBufferSize)` and paints nothing.
## Properties

The struct holds the value of each
[property declared](../../language/properties/#sls.prop.decl.form) on the
[property declared](/language/properties/#sls.prop.decl.form) on the
root element in a private field. \{#sls.gen.prop.field}

Kebab-case property names become snake_case in the generated names:
Expand Down
1 change: 1 addition & 0 deletions docs/safety/src/content/docs/reference/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
title: API Reference
description: Reference for the Slint SC certified subset of the .slint language.
slug: reference
normative: false
---

This section documents the elements, properties, callbacks, functions, structs
Expand Down
2 changes: 1 addition & 1 deletion docs/safety/src/content/docs/reference/rendering.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: How a window is rendered into a frame buffer.
---

The application renders by calling the `render_rgb8` function of the
[generated code](../generated-code/).
[generated code](/reference/generated-code/). \{#sls.paint.invocation}

## Model

Expand Down
18 changes: 9 additions & 9 deletions docs/safety/src/content/docs/requirements/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ Each Requirement under this Section has a descriptive ID that begins with SR_, a

All ISO26262 references below are valid for the 2018 edition of the standard.

* ISO 26262-4 5.x: See [Development Phases](../development-phases/).
* ISO 26262-4 9.x: See [Validation](../qualification-plan/validation/).
* ISO 26262-6 7.x: See [Architecture Design](../using-slint-sc/#slint-sc-architecture-design-iso-262626-74)
* ISO 26262-8 5.x: See [Distributed Development](../development-process/#distributed-development-iso-26262-8-5x)
* ISO 26262-4 5.x: See [Development Phases](/development-phases/).
* ISO 26262-4 9.x: See [Validation](/qualification-plan/validation/).
* ISO 26262-6 7.x: See [Architecture Design](/using-slint-sc/#slint-sc-architecture-design-iso-262626-74)
* ISO 26262-8 5.x: See [Distributed Development](/development-process/#distributed-development-iso-26262-8-5x)
* ISO 26262-8 6.4: The safety requirements shall be traceable to the safety goals and to the safety concept. The traceability shall be documented and maintained.
* ISO 26262-8 7.x: See [Configuration Management](../development-process/#configuration-management-iso-26262-8-7x)
* ISO 26262-8 8.x: See [Change Management](../development-process/#change-management-iso-26262-8-8x)
* ISO 26262-8 9.4.x: See [Verification](../development-process/#verification-iso-26262-8-94x)
* ISO 26262-8 11.4.8: See [The Development Process](../development-process/#the-development-process-iso-26262-8-1148)
* ISO 26262-8 12.x: See [Software Component Qualification](../development-process/#software-component-qualification-iso-26262-8-12x)
* ISO 26262-8 7.x: See [Configuration Management](/development-process/#configuration-management-iso-26262-8-7x)
* ISO 26262-8 8.x: See [Change Management](/development-process/#change-management-iso-26262-8-8x)
* ISO 26262-8 9.4.x: See [Verification](/development-process/#verification-iso-26262-8-94x)
* ISO 26262-8 11.4.8: See [The Development Process](/development-process/#the-development-process-iso-26262-8-1148)
* ISO 26262-8 12.x: See [Software Component Qualification](/development-process/#software-component-qualification-iso-26262-8-12x)

Loading
Loading