Skip to content

Commit

Permalink
version-modified directives
Browse files Browse the repository at this point in the history
  • Loading branch information
irskep committed Aug 28, 2024
1 parent e9be008 commit 73b2077
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/src/basics/configuration.dj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
order: -1
---
{#config-reference}
# Configuration reference

{tag=aside .caution}
Expand Down
43 changes: 43 additions & 0 deletions docs/src/features/version_directives.dj
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Version directives

{added-in-version="0.0.5"}
:::
:::

You can mark some content as applying to a specific version using the `added-in-version` attribute on an empty div. If you specified a `projectInfo.version`{.language-yaml} in your [configuration](#config-reference), and the specified version is greater, then the message will be future-tense.

```djot
{added-in-version="0.0.1"}
:::
:::

{added-in-version="10.0"}
:::
:::
```

Output:

{added-in-version="0.0.1"}
:::
:::

{added-in-version="10.0"}
:::
:::

You can also put content inside if you want to add commentary.

```djot
{added-in-version="10.0"}
:::
We're really excited about this feature!
:::
```

Output:

{added-in-version="10.0"}
:::
We're really excited about this feature!
:::
2 changes: 2 additions & 0 deletions src/engine/executeConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { SyntaxHighlightingPlugin } from "../plugins/syntaxHighlighting.js";
import { fileURLToPath } from "url";
import { IndextermsPlugin } from "../plugins/indextermsPlugin.js";
import { GFMAlertsPlugin } from "../plugins/gfmAlertsPlugin.js";
import { VersionDirectivesPlugin } from "../plugins/versionDirectives.js";

function pluralize(n: number, singular: string, plural: string): string {
return n === 1 ? `1 ${singular}` : `${n} ${plural}`;
Expand All @@ -37,6 +38,7 @@ function makeBuiltinPlugins(config: DjockeyConfigResolved): DjockeyPlugin[] {
new AutoTitlePlugin(),
new SyntaxHighlightingPlugin(config),
new GFMAlertsPlugin(),
new VersionDirectivesPlugin(config),
];
}

Expand Down
84 changes: 84 additions & 0 deletions src/plugins/versionDirectives.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { DjockeyConfig, DjockeyDoc, DjockeyPlugin } from "../types.js";
import { applyFilter } from "../engine/djotFiltersPlus.js";
import { getAnyAttribute } from "../util.js";
import { Block } from "@djot/djot";

function normalizeSemverString(semverString: string): [number, number, number] {
const parts: number[] = semverString
.split(".")
.map((s) => parseInt(s, 10))
.slice(0, 3);
while (parts.length < 3) parts.push(0);
return parts as [number, number, number];
}

function isSemverGreaterOrEqual(a: string, b: string): boolean {
const aParts = normalizeSemverString(a);
const bParts = normalizeSemverString(b);
for (let i = 0; i < 3; i++) {
if (aParts[i] > bParts[i]) return true;
if (aParts[i] < bParts[i]) return false;
}
return true;
}

type Cls =
| "added-in-version"
| "changed-in-version"
| "removed-in-version"
| "deprecated-in-version";
const PREFIXES_CURRENT: Record<Cls, string> = {
"added-in-version": "Added in version",
"changed-in-version": "Changed in version",
"removed-in-version": "Removed in version",
"deprecated-in-version": "Deprecated in version",
};

const PREFIXES_FUTURE: Record<Cls, string> = {
"added-in-version": "Will be added in version",
"changed-in-version": "Will be changed in version",
"removed-in-version": "Will be reemoved in version",
"deprecated-in-version": "Will be deprecated in version",
};

export class VersionDirectivesPlugin implements DjockeyPlugin {
name = "Version Directives";

constructor(public config: DjockeyConfig) {}

onPass_write(doc: DjockeyDoc) {
const projectVersion = this.config.projectInfo?.version;
applyFilter(doc.docs.content, () => ({
div: (node) => {
const keyAndValue = getAnyAttribute(
node,
Object.keys(PREFIXES_CURRENT)
);
if (!keyAndValue) return;
const [key, value] = keyAndValue;

// If no project version, assume it's already release. Otherwise, put it in the future.
const prefix =
!projectVersion || isSemverGreaterOrEqual(projectVersion, value)
? PREFIXES_CURRENT[key as Cls]
: PREFIXES_FUTURE[key as Cls];
return buildAST(key, `${prefix} ${value}`, node.children);
},
}));
}
}

function buildAST(cls: string, text: string, children: Block[]): Block {
return {
tag: "div",
attributes: { class: `version-modified ${cls}` },
children: [
{
tag: "para",
attributes: { class: "primary" },
children: [{ tag: "str", text }],
},
...structuredClone(children),
],
};
}
16 changes: 16 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ export function getHasClass(node: HasAttributes, cls: string): boolean {
return values.has(cls);
}

export function getAttribute(node: HasAttributes, k: string): string | null {
if (!node.attributes || node.attributes[k] === undefined) return null;
return node.attributes[k];
}

export function getAnyAttribute(
node: HasAttributes,
keys: string[]
): [string, string] | null {
if (!node.attributes) return null;
for (const k of keys) {
if (node.attributes[k] !== undefined) return [k, node.attributes[k]];
}
return null;
}

export function makeStubDjotDoc(children: Block[]): Doc {
return {
tag: "doc",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* ASIDES */

aside {
border-width: var(--aside-border-width);
border-radius: var(--aside-border-radius);
Expand Down Expand Up @@ -75,3 +77,18 @@ aside.warning::before {
aside.warning {
border-color: var(--aside-color-border-danger);
}

/* VERSION MODIFIED */

.version-modified {
font-style: italic;
}

.version-modified p.primary {
font-size: var(--fs-medium);
margin-bottom: 0.5em;
}

.version-modified p {
font-size: var(--fs-small);
}

0 comments on commit 73b2077

Please sign in to comment.