Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🎁 Use commonDirectiveOptions in all directives #1650

Merged
merged 14 commits into from
Nov 20, 2024
Merged
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great to be using this here now! Thank you!!

:::

## 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} exercise
fwkoch marked this conversation as resolved.
Show resolved Hide resolved
:::

## 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gets added to the node now too!

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
Loading