Skip to content

Commit

Permalink
Feat: support eval directives/roles (#41)
Browse files Browse the repository at this point in the history
* Feat: add expr support

* Fix: temporarily disable skip_if_exists

* Fix: fix build

* Fix: build

* Fix: use python path for artifacts
  • Loading branch information
agoose77 committed Jun 11, 2022
1 parent ca0f746 commit b21459a
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 69 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ jobs:
set -eux
jlpm
jlpm run eslint:check
- name: Check manifest
run: check-manifest -v
- name: Build SDist
run: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,4 @@ dmypy.json

# OSX files
.DS_Store
.idea
11 changes: 9 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ dynamic = [
"version",
]

[project.optional-dependencies]
eval = [
"jupyterlab-imarkdown>=0.2.0a0",
]

[project.urls]
Homepage = "https://github.com/executablebooks/jupyterlab-myst"

Expand All @@ -49,13 +54,15 @@ source = "nodejs"

[tool.hatch.build.targets.wheel.shared-data]
"install.json" = "share/jupyter/labextensions/jupyterlab-myst/install.json"
"jupyterlab-myst/labextension" = "share/jupyter/labextensions/jupyterlab-myst"
"jupyterlab_myst/labextension" = "share/jupyter/labextensions/jupyterlab-myst"

[tool.hatch.build.targets.sdist]
artifacts = ["jupyterlab_myst/labextension"]

[tool.hatch.build.hooks.jupyter-builder]
dependencies = ["hatch-jupyter-builder"]
build-function = "hatch_jupyter_builder.npm_builder"
skip_if_exists = ["jupyterlab_myst/labextension/static/style.js"]
#skip_if_exists = ["jupyterlab_myst/labextension/static/style.js"] TODO - wait for fix in hatch-jupyter-builder
ensured-targets = ["jupyterlab_myst/labextension/static/style.js", "jupyterlab_myst/labextension/package.json"]
install-pre-commit-hook = true

Expand Down
64 changes: 0 additions & 64 deletions src/builtins/docutils.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/builtins/docutils/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const EXPR_CLASS = 'eval-expr';
78 changes: 78 additions & 0 deletions src/builtins/docutils/directives.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
directiveOptions,
IDirectiveData,
directivesDefault,
Directive
} from 'markdown-it-docutils';
import { EXPR_CLASS } from './constants';
import type Token from 'markdown-it/lib/token';

const Figure = directivesDefault['figure'];
const { uri } = directiveOptions;

export class EvalDirectiveAny extends Directive {
public required_arguments = 1;
public optional_arguments = 0;
public final_argument_whitespace = false;
public has_content = false;
public rawOptions = true;

run(data: IDirectiveData<keyof EvalDirectiveAny['option_spec']>): Token[] {
// TODO store options and the fact that this is a code cell rather than a fence?
const token = this.createToken('expr', 'input', 0, {
content: data.body,
map: data.bodyMap
});
const expr = uri(data.args[0] || '');
token.attrSet('type', 'hidden');
token.attrSet('class', EXPR_CLASS);
token.attrSet('value', expr);
return [token];
}
}

/** Directive for parsing code outputs from notebooks, wrapped in a figure.
*
* Adapted from: docutils/docutils/parsers/rst/directives/images.py
*/
export class EvalFigureDirective extends Figure {
create_image(
data: IDirectiveData<keyof EvalFigureDirective['option_spec']>
): Token {
// get URI
const expr = uri(data.args[0] || '');
const token = this.createToken('expr', 'input', 0, {
map: data.map,
block: true
});
token.attrSet('type', 'hidden');
token.attrSet('class', EXPR_CLASS);
token.attrSet('value', expr);
token.attrSet('alt', data.options.alt || '');
// TODO markdown-it default renderer requires the alt as children tokens
const altTokens: Token[] = [];
if (data.options.alt) {
this.state.md.inline.parse(
data.options.alt,
this.state.md,
this.state.env,
altTokens
);
}
token.children = altTokens;
if (data.options.height) {
token.attrSet('height', data.options.height);
}
if (data.options.width) {
token.attrSet('width', data.options.width);
}
if (data.options.align) {
token.attrJoin('class', `align-${data.options.align}`);
}
if (data.options.class) {
token.attrJoin('class', data.options.class.join(' '));
}

return token;
}
}
80 changes: 80 additions & 0 deletions src/builtins/docutils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { simpleMarkdownItPlugin } from '@agoose77/jupyterlab-markup';
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
import type MarkdownIt from 'markdown-it';
import katex from 'katex';

import { PACKAGE_NS } from '../../tokens';
import { EvalRole } from './roles';
import { EvalDirectiveAny, EvalFigureDirective } from './directives';

import {
directivesDefault,
rolesDefault,
IOptions
} from 'markdown-it-docutils';

/**
* Provides docutils roles and directives
*/
export const docutils: JupyterFrontEndPlugin<void> = simpleMarkdownItPlugin(
PACKAGE_NS,
{
id: 'markdown-it-docutils',
title: 'Docutils',
description:
'Plugin for implementing docutils style roles (inline extension point) and directives (block extension point)',
documentationUrls: {
Plugin: 'https://github.com/executablebooks/markdown-it-docutils'
},
examples: {
'Example ': '```{name} argument\n:option: value\n\ncontent\n```'
},
plugin: async () => {
const docutilsPlugin = await import(
/* webpackChunkName: "markdown-it-docutils" */ 'markdown-it-docutils'
);
function wrappedDocutilsPlugin(md: MarkdownIt, options: IOptions) {
const roles = {
...(options?.roles ?? rolesDefault),
eval: EvalRole
};
const directives = {
...(options?.directives ?? directivesDefault),
'eval:figure': EvalFigureDirective,
eval: EvalDirectiveAny
};

docutilsPlugin.default(md, {
...options,
roles: roles,
directives: directives
});

// Add renderers to MarkdownIt
md.renderer.rules['math_block'] = (tokens, idx) => {
const token = tokens[idx];
const content = token.content.trim();
const rendered = katex.renderToString(content, {
displayMode: true,
throwOnError: false,
output: 'htmlAndMathml'
});
return `<div class="${token.attrGet('class')}">${rendered}</div>`;
};

md.renderer.rules['math_inline'] = (tokens, idx) => {
const token = tokens[idx];
const content = token.content.trim();
const rendered = katex.renderToString(content, {
displayMode: false,
throwOnError: false,
output: 'htmlAndMathml'
});
return `<span class="${token.attrGet('class')}">${rendered}</span>`;
};
}

return [wrappedDocutilsPlugin];
}
}
);
14 changes: 14 additions & 0 deletions src/builtins/docutils/roles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { IRoleData, Role } from 'markdown-it-docutils';
import { EXPR_CLASS } from './constants';
import type Token from 'markdown-it/lib/token';

export class EvalRole extends Role {
run(data: IRoleData): Token[] {
const inline = new this.state.Token('expr', 'input', 0);
inline.attrSet('class', EXPR_CLASS);
inline.attrSet('type', 'hidden');
inline.attrSet('value', data.content);
inline.content = data.content;
return [inline];
}
}

0 comments on commit b21459a

Please sign in to comment.