Skip to content

Commit

Permalink
🎁 Use commonDirectiveOptions in all directives (#1650)
Browse files Browse the repository at this point in the history
Co-authored-by: Franklin Koch <franklinwkoch@gmail.com>
  • Loading branch information
agoose77 and fwkoch authored Nov 20, 2024
1 parent b589574 commit d7a6fdd
Show file tree
Hide file tree
Showing 26 changed files with 346 additions and 240 deletions.
9 changes: 9 additions & 0 deletions .changeset/twenty-rocks-divide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"myst-ext-exercise": patch
"myst-directives": patch
"myst-ext-proof": patch
"myst-ext-grid": patch
"myst-ext-tabs": patch
---

Add support for commonDirectiveOptions in all directives
33 changes: 23 additions & 10 deletions docs/directives.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,25 @@ import { u } from 'unist-builder';
import { mystParse } from 'myst-parser';
import { defaultDirectives } from 'myst-directives';
import { defaultRoles } from 'myst-roles';
import { cardDirective } from 'myst-ext-card';
import { gridDirectives } from 'myst-ext-grid';
import { proofDirective } from 'myst-ext-proof';
import { exerciseDirectives } from 'myst-ext-exercise';
import { reactiveDirective, reactiveRole } from 'myst-ext-reactive';
import { tabDirectives } from 'myst-ext-tabs';
import { fileError } from 'myst-common';

const allDirectives = [
...defaultDirectives,
...gridDirectives,
...exerciseDirectives,
...tabDirectives,
reactiveDirective,
cardDirective,
proofDirective,
];
const allRoles = [...defaultRoles, reactiveRole];

/**
* @param {import('myst-common').OptionDefinition} option
*/
Expand Down Expand Up @@ -74,7 +91,7 @@ const mystDirective = {
},
run(data, vfile) {
const name = data.arg;
const directive = defaultDirectives.find((d) => d.name === name);
const directive = allDirectives.find((d) => d.name === name);
if (!directive) {
fileError(vfile, `myst:directive: Unknown myst directive "${name}"`);
return [];
Expand Down Expand Up @@ -130,7 +147,7 @@ const mystRole = {
},
run(data, vfile) {
const name = data.arg;
const role = defaultRoles.find((d) => d.name === name);
const role = allRoles.find((d) => d.name === name);
if (!role) {
fileError(vfile, `myst:role: Unknown myst role "${name}"`);
return [];
Expand Down Expand Up @@ -178,17 +195,15 @@ const mystDirectiveRole = {
const [, modified, rawLabel] = match ?? [];
const label = rawLabel ?? data.body;
const [name, opt] = label?.split('.') ?? [];
const directive = defaultDirectives.find((d) => d.name === name || d.alias?.includes(name));
const directive = allDirectives.find((d) => d.name === name || d.alias?.includes(name));
const identifier = opt
? `directive-${directive?.name ?? name}-${opt}`
: `directive-${directive?.name ?? name}`;
var textToDisplay = modified?.trim() || name;
if (opt) {
textToDisplay = `${textToDisplay}.${opt}`;
}
return [
u('crossReference', { identifier }, [u('inlineCode', `{${textToDisplay}}`)]),
];
return [u('crossReference', { identifier }, [u('inlineCode', `{${textToDisplay}}`)])];
},
};

Expand All @@ -208,15 +223,13 @@ const mystRoleRole = {
const [, modified, rawLabel] = match ?? [];
const label = rawLabel ?? data.body;
const [name, opt] = label?.split('.') ?? [];
const role = defaultRoles.find((d) => d.name === name || d.alias?.includes(name));
const role = allRoles.find((d) => d.name === name || d.alias?.includes(name));
const identifier = opt ? `role-${role?.name ?? name}-${opt}` : `role-${role?.name ?? name}`;
var textToDisplay = modified?.trim() || name;
if (opt) {
textToDisplay = `${textToDisplay}.${opt}`;
}
return [
u('crossReference', { identifier }, [u('inlineCode', `{${textToDisplay}}`)]),
];
return [u('crossReference', { identifier }, [u('inlineCode', `{${textToDisplay}}`)])];
},
};

Expand Down
105 changes: 46 additions & 59 deletions docs/exercises.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,10 @@ thumbnail: ./thumbnails/exercise.png

There are two directives available to add exercises and solutions to your documents: (1) an `exercise` directive; and (2) a `solution` directive. The exercises are enumerated by default and can take in an optional title argument as well as be "gated" around Jupyter Notebook cells.

:::{note} Same as Sphinx Exercise 🎉
:class: dropdown

The implementation and documentation for exercises and solutions is based on [Sphinx Exercise](https://ebp-sphinx-exercise.readthedocs.io), the syntax can be used interchangeably. We have reused the examples in that extension here to show off the various parts of the MyST extension.

Changes to the original extension include being able to click on the exercise label (e.g. "Exercise 1"), and having a link to that exercise anchor. We have also updated the styles from both Sphinx and Jupyter Book to be more distinct from admonitions.

You can also reference exercises with any cross-reference syntax (including the {myst:role}`ref` and {myst:role}`numref` roles). We recommend the markdown link syntax.
:::

## Exercise Directive

**Example**
::::{tab-set}
:::{tab-item} Example

```{exercise}
:label: my-exercise
Expand All @@ -34,7 +25,8 @@ In particular, write a function `factorial` such that `factorial(n)` returns $n!
for any positive integer $n$.
```

**MyST Syntax**
:::
:::{tab-item} MyST Syntax

````markdown
```{exercise}
Expand All @@ -51,31 +43,22 @@ for any positive integer $n$.
```
````

_Source:_ [QuantEcon](https://python-programming.quantecon.org/functions.html#Exercise-1)

The following options for exercise and solution directives are supported:

- `label`: text

A unique identifier for your exercise that you can use to reference it with a Markdown link or {myst:role}`ref` and {myst:role}`numref` roles. Cannot contain spaces or special characters.

- `class`: text

Value of the exercise’s class attribute which can be used to add custom CSS or JavaScript. This can also be the optional `dropdown` class to initially hide the exercise.

- `nonumber`: flag (empty)
:::
::::

Turns off exercise auto numbering.
_Source:_ [QuantEcon](https://python-programming.quantecon.org/functions.html#Exercise-1)

- `hidden` : flag (empty)
The following options for exercise directives are supported:

Removes the directive from the final output.
:::{myst:directive} exercise
:::

## Solution Directive

A solution directive can be included using the `solution` pattern. It takes in the label of the directive it wants to link to as a required argument. Unlike the `exercise` directive, the solution directive is not enumerable as it inherits numbering directly from the linked exercise. The argument for a solution is the label of the linked exercise, which is required.

**Example**
::::{tab-set}
:::{tab-item} Example

````{solution} my-exercise
:label: my-solution
Expand All @@ -93,7 +76,8 @@ factorial(4)
```
````

**MyST Syntax**
:::
:::{tab-item} MyST Syntax

`````markdown
````{solution} my-exercise
Expand All @@ -113,21 +97,15 @@ factorial(4)
````
`````

_Source:_ [QuantEcon](https://python-programming.quantecon.org/functions.html#Exercise-1)

The following options are also supported:

- `label` : text

A unique identifier for your solution that you can use to reference it with `{ref}`. Cannot contain spaces or special characters.

- `class` : text
:::
::::

Value of the solution’s class attribute which can be used to add custom CSS or JavaScript.
_Source:_ [QuantEcon](https://python-programming.quantecon.org/functions.html#Exercise-1)

- `hidden` : flag (empty)
The following options for solution directives are supported:

Removes the directive from the final output.
:::{myst:directive} solution
:::

## Referencing Exercises & Solutions

Expand Down Expand Up @@ -170,7 +148,7 @@ In the event that the directive being referenced is unenumerable, the reference

```{exercise} $n!$ Factorial
:label: nfactorial
:nonumber:
:enumerated: false
Write a function `factorial` such that `factorial(int n)` returns $n!$
for any positive integer $n$.
Expand Down Expand Up @@ -201,7 +179,7 @@ If the title of the linked directive being reference does not exist, it will def

```{exercise}
:label: nfactorial-notitle
:nonumber:
:enumerated: false
Write a function `factorial` such that `factorial(int n)` returns $n!$
for any positive integer $n$.
Expand Down Expand Up @@ -238,44 +216,53 @@ to include in an exercise or solution admonition.
```

**Basic Syntax**
::::{tab-set}
:::{tab-item} Example

````markdown
```{exercise-start}
:label: ex1
```

```python
# Some code to explain the figure
# Some setup code that needs executing
```

and maybe you wish to add a figure

```{figure} https://github.com/rowanc1/pics/blob/main/beach.png
```{figure} https://github.com/rowanc1/pics/blob/main/sunset.png
```

```{exercise-end}
```
````

:::

:::{tab-item} MyST Syntax

````markdown
```{exercise-start}
:label: ex1
```

```python
# Some setup code that needs executing
# Some code to explain the figure
```

and maybe you wish to add a figure

```{figure} https://github.com/rowanc1/pics/blob/main/sunset.png
```{figure} https://github.com/rowanc1/pics/blob/main/beach.png

```

```{exercise-end}

```
````

:::
::::

This can also be completed for solutions with `solution-start` and `solution-end` directives. The `solution-start` and `exercise-start` directives have the same options as original directive.

Expand All @@ -289,7 +276,8 @@ alongside feedback to diagnose the issue in document structure.

To visually hide the content, simply add `:class: dropdown` as a directive option, similar to an admonition.

**Example**
::::{tab-set}
:::{tab-item} Example

```{exercise}
:class: dropdown
Expand All @@ -304,7 +292,9 @@ In particular, write a function `factorial` such that `factorial(n)` returns $n!
for any positive integer $n$.
```

**MyST Syntax**:
:::

:::{tab-item} MyST Syntax

````markdown
```{exercise}
Expand All @@ -321,23 +311,20 @@ for any positive integer $n$.
```
````

:::
::::

### Remove Directives

Any specific directive can be hidden by introducing the `:hidden:` option. For example, the following example will not be displayed

````markdown
````{myst}
```{exercise}
:hidden:
This is a hidden exercise directive.
```
````

```{exercise}
:hidden:
This is a hidden exercise directive.
```

% TODO: Remove All Solutions
% TODO: Custom CSS
1 change: 0 additions & 1 deletion packages/myst-directives/src/admonition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ export const admonitionDirective: DirectiveSpec = {
data.name !== 'admonition'
? (data.name.replace('.callout-', '') as Admonition['kind'])
: undefined,
class: data.options?.class as string,
children: children as any[],
};
if (data.options?.icon === false) {
Expand Down
9 changes: 2 additions & 7 deletions packages/myst-directives/src/aside.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { DirectiveSpec, DirectiveData, GenericNode } from 'myst-common';
import type { Aside } from 'myst-spec-ext';
import type { FlowContent, ListContent, PhrasingContent } from 'myst-spec';
import { addCommonDirectiveOptions, labelDirectiveOption } from './utils.js';
import { addCommonDirectiveOptions, commonDirectiveOptions } from './utils.js';

export const asideDirective: DirectiveSpec = {
name: 'aside',
Expand All @@ -11,11 +11,7 @@ export const asideDirective: DirectiveSpec = {
doc: 'An optional title',
},
options: {
...labelDirectiveOption('aside'),
// TODO: Add enumeration in future
class: {
type: String,
},
...commonDirectiveOptions('aside'),
},
body: {
type: 'myst',
Expand All @@ -34,7 +30,6 @@ export const asideDirective: DirectiveSpec = {
kind:
data.name == 'aside' || data.name == 'margin' ? undefined : (data.name as Aside['kind']),
children,
class: data.options?.class as string | undefined,
};
addCommonDirectiveOptions(data, aside);
return [aside];
Expand Down
14 changes: 8 additions & 6 deletions packages/myst-directives/src/bibliography.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import type { DirectiveSpec, DirectiveData, GenericNode } from 'myst-common';
import { addCommonDirectiveOptions, commonDirectiveOptions } from './utils.js';

export const bibliographyDirective: DirectiveSpec = {
name: 'bibliography',
options: {
...commonDirectiveOptions('bibliography'),
filter: {
type: String,
},
},
run(data: DirectiveData): GenericNode[] {
return [
{
type: 'bibliography',
filter: data.options?.filter,
},
];
const bibliography = {
type: 'bibliography',
filter: data.options?.filter,
};
addCommonDirectiveOptions(data, bibliography);
return [bibliography];
},
};
Loading

0 comments on commit d7a6fdd

Please sign in to comment.