-
Notifications
You must be signed in to change notification settings - Fork 125
🎨 Markdown formatting improvements #2245
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "myst-parser": patch | ||
| --- | ||
|
|
||
| Add body/options to the mystDirective and mystRole nodes |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "myst-to-md": patch | ||
| --- | ||
|
|
||
| Improve formatting of raw myst-directives |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,6 @@ import type { | |
| DirectiveData, | ||
| DirectiveSpec, | ||
| DirectiveContext, | ||
| ParseTypes, | ||
| GenericParent, | ||
| } from 'myst-common'; | ||
| import { RuleId, fileError, fileWarn } from 'myst-common'; | ||
|
|
@@ -94,6 +93,7 @@ export function applyDirectives( | |
| if (argSpec.required && data.arg == null) { | ||
| validationError = true; | ||
| } | ||
| node.args = data.arg; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the biggest change is that we put the arg, options and body of roles and directives onto the This shouldn't matter for the duplication as we remove these nodes early in the processing for most things. But it is really helpful to have them for the formatting / writing of markdown. We might want to change it to @fwkoch or @agoose77 if you have opinions here, that would help.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok - seeing this, my first impression was "we already stash this stuff on the On the initial parse pass, we do save So, the change in this PR is taking the parsed, previously transient arg/option/body values and saving them directly to the Getting back to your specific question @rowanc1 - are we putting this in the right place? The biggest issue is that we are clobbering raw I guess the other argument against clobbering the raw values with parsed values is simply backwards compatibility. This is a backwards incompatible change that I don't think will impact anyone, but there could be something somewhere using these raw Regarding the alternatives you suggest:
So maybe it's just fine as-implemented? (We could potentially even stop creating the TL;DR: 👍 to your change, maybe minor version bump. |
||
| } | ||
| } else if (argNode) { | ||
| const message = `unexpected argument provided for directive: ${name}`; | ||
|
|
@@ -104,6 +104,7 @@ export function applyDirectives( | |
| // const options: Record<string, ParseTypes> = {}; | ||
| const { valid: validOptions, options } = parseOptions(name, node, vfile, optionsSpec); | ||
| data.options = options; | ||
| node.options = options; | ||
| validationError = validationError || validOptions; | ||
|
|
||
| // Handle body | ||
|
|
@@ -124,6 +125,7 @@ export function applyDirectives( | |
| `body of directive: ${name}`, | ||
| RuleId.directiveBodyCorrect, | ||
| ); | ||
| node.body = data.body; | ||
| if (bodySpec.required && data.body == null) { | ||
| validationError = true; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
| import { toText, fileError, RuleId } from 'myst-common'; | ||
| import { select, selectAll } from 'unist-util-select'; | ||
| import type { VFile } from 'vfile'; | ||
| import { longestStreak } from 'longest-streak'; | ||
| import type { NestedState, Parent, Validator } from './types.js'; | ||
| import { incrementNestedLevel, popNestedLevel } from './utils.js'; | ||
|
|
||
|
|
@@ -74,12 +75,45 @@ | |
| } | ||
|
|
||
| /** | ||
| * Generic MyST directive handler | ||
| * | ||
| * This uses the directive name/args/value and ignores any children nodes | ||
| * Handler for a raw directive | ||
| */ | ||
| function mystDirective(node: any, _: Parent, state: NestedState, info: Info): string { | ||
| return writeStaticDirective(node.name, { argsKey: 'args' })(node, _, state, info); | ||
| function writeDirective(options?: DirectiveOptions) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I'm feeling a little confused about this. First, I don't think we need this wrapper "factory function" - Second, we are only using |
||
| return (node: any, _: Parent, state: NestedState, info: Info): string => { | ||
| incrementNestedLevel('directive', state); | ||
| const { label, class: className, ...otherOptions } = node.options ?? {}; | ||
| const optLabel = label ? `#${label}` : ''; | ||
| const optClass = className | ||
| ? className | ||
| .split(' ') | ||
| .filter((c: string) => c.trim() !== '') | ||
| .map((c: string) => `.${c.trim()}`) | ||
| .join(' ') | ||
| : ''; | ||
| const optOther = Object.entries(otherOptions) | ||
| .map(([key, value]) => `${key}=${value}`) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Writing all options this way feels risky. Should we check if |
||
| .join(' '); | ||
| const directiveOpts = [optLabel, optClass, optOther].filter(Boolean).join(' '); | ||
|
|
||
| // If the directive has a body which is an array, use it as content. | ||
| // Otherwise, use the value. | ||
| const markdownBody = node.body && Array.isArray(node.body); | ||
| const content = markdownBody | ||
| ? state.containerFlow({ type: 'root', children: node.body }, info) | ||
| : node.value; | ||
|
|
||
| const nesting = popNestedLevel('directive', state); | ||
| const markerChar = markdownBody ? ':' : '`'; | ||
| const markerLength = Math.max(longestStreak(content, markerChar) + 1, nesting + 3); | ||
| const marker = markerChar.repeat(markerLength); | ||
| const directiveInline = [node.name, directiveOpts].filter(Boolean).join(' '); | ||
| const args = Array.isArray(node.args) | ||
| ? state.containerPhrasing({ type: 'heading', depth: 1, children: node.args }, info) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't get this - Are we injecting this |
||
| : node.args; | ||
| const directiveLines = [`${marker}{${directiveInline}}${args ? ' ' : ''}${args ? args : ''}`]; | ||
| if (content) directiveLines.push(content); | ||
| directiveLines.push(marker); | ||
| return directiveLines.join('\n'); | ||
| }; | ||
| } | ||
|
|
||
| const CODE_BLOCK_KEYS = [ | ||
|
|
@@ -388,6 +422,13 @@ | |
| return writeFlowDirective(name, args, options)(nodeCopy, _, state, info); | ||
| } | ||
|
|
||
| function math(node: any, _: Parent, state: NestedState, info: Info): string { | ||
| if (!node.typst && !node.label) { | ||
| return `$$\n${node.value}\n$$`; | ||
| } | ||
| return writeStaticDirective('math', { keys: ['label', 'typst'] })(node, _, state, info); | ||
| } | ||
|
|
||
| export const directiveHandlers: Record<string, Handle> = { | ||
| code, | ||
| image, | ||
|
|
@@ -403,11 +444,11 @@ | |
| }), | ||
| tabSet: writeFlowDirective('tab-set'), | ||
| tabItem, | ||
| math: writeStaticDirective('math', { keys: ['label'] }), | ||
| math, | ||
| embed: writeStaticDirective('embed', { argsKey: 'label' }), | ||
| include: writeStaticDirective('include', { argsKey: 'file' }), | ||
| mermaid: writeStaticDirective('mermaid'), | ||
| mystDirective, | ||
| mystDirective: writeDirective(), | ||
| }; | ||
|
|
||
| export const directiveValidators: Record<string, Validator> = { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests in the extension code are made a bit less strict here. No changes to the code, so no need to bump the packages.