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

remark@next (13) #22

Open
wooorm opened this issue Oct 3, 2020 · 10 comments
Open

remark@next (13) #22

wooorm opened this issue Oct 3, 2020 · 10 comments

Comments

@wooorm
Copy link

wooorm commented Oct 3, 2020

Hi!

remark is switching to a new parser (and compiler) internally (micromark, remarkjs/remark#536), which will break this plugin.
Keep an eye out for remark-footnotes, remark-frontmatter, remark-gfm, remark-math, and other syntax plugins similar to this one, which could serve as inspiration for this plugin. Typically, these syntax plugins will be small wrapper code around micromark and mdast extensions btw. Also: feel free to ask me questions!

@arobase-che
Copy link
Owner

arobase-che commented Oct 28, 2020

Ok, so I took the night to study the problem.

If I understand well, this plugin is obsolete. The parsing is now done in micromark and than I can't rewrite the tokenizers of remark-parse.

I wonder what to do. How should I support such a syntax ? Basically everything is outdated and I must rewrite a plugin from scratch and first study micromark. It's a lot of hard work.

A hint on how to do it well will be very welcome. I guess that you don't remember what that plugin does and how it works. Here a little summary :

This plugin extends the tokenizers of remark-parse to support syntax like that : *Hello !*{ style="red"}. A new tokenizer is created, it call the old tokenizer and them parse the { style="red"} syntax; and modify the result of the old tokenizer consequently (adding attributes).

So now, they is no more tokernizer to extend. Should I extend the state machine of micromark (SyntaxExtension ?) or do it in the unified/remark ecosystem (maybe hard or near to impossible due to edge-cases as *Hello !*{ comment="I _like_ the work on remark"} where the AST will be complex ) ?

Ok, thinking about it made me realize that I only one main question. I think, i have no chose but to extend micromark. Do you think it's possible ? Is there any doc available about it or I will have to study the source code ?

Thank for improving the remark ecosystem and in advance to any help provided.

@wooorm
Copy link
Author

wooorm commented Oct 29, 2020

For generic directives, I wrote an attribute parser, that support #id.class boolean key=value or="quoted" attributes: https://github.com/micromark/micromark-extension-directive/blob/main/lib/factory-attributes.js. That’s a lot of what you’d need to write.

Architecturally, there is a) a tokenizing part, where you parse {attributes} in micromark, and b) a tree building part, where you turn those tokens into mdast—for directives, that’s mdast-util-directive/from-markdown.js. Last, the remark part bundles it all so that users don’t need to know about everything.

I like building things in separate projects. But you don’t have to: it can all live in this project. The reasons why I split things up, are a) just nice coding for me, b) not everyone needs a syntax tree, so I also have micromark html extensions, c) in the future there will be a CST, not sure how or when that would be, but mdast wouldn’t be the only tree format.

For the tokenizing part, I think you’d be looking for {, then try and parse attributes, then find a }, and you‘re done! With one exception, which I’ll describe later. The second thing is in the adapter, where you‘re taking tokens and building trees, to take your attribute tokens and add them to the preceding link or heading. mdast-util-directive has attributes which aren’t hast, but the remark-directive readme shows an example of making hProperties out of ’em.

The complex part for attrs is that they’re added to other constructs. You can access the events on micromark, so that you can check to parse attributes or not based on whether there’s a link or image right before it. Links and images are made up of a start (![, [), and an end (](asd), ][asd], ][], or ]), which is done here: https://github.com/micromark/micromark/blob/ed4d78e4e2a799858d2c2c4f87b09163f1eae9da/lib/tokenize/label-end.js#L164. Those and autolinks, code spans, footnote calls, inline footnotes, are pretty easy. But emphasis, strong, and strikethrough are harder because those sequences can open and close, which is figured out at the end.

For the flow part (blocks), I see that you have two different ways: setext headings attributes on their own line, ATX headings inline. I would suggest doing only the setext heading way: a “block” attribute starts at a line, cannot include line endings, and ends at a line ending.
Then, it’s attached to the preceding block, whether it’s a paragraph, a heading (ATX or setext), thematic breaks, code (fenced or indented). Containers (lists and block quotes) would be more complex, but as you’re not handling them yet, we can skip that for now.


Alright, already a wall of text, so I’ll quit now. That should give you some ideas on how to go about it.
An alternative is: The downside of attributes is that they don’t work in other markdown parsers. More markdown parsers are moving to generic directives. You could push folks to instead switch to a directive based approach? Such as the example in the remark-directive readme.

@janosh
Copy link

janosh commented Dec 14, 2020

What's the current status? Is there a new remark plugin on the horizon that will provide the functionality of this one with the new micromark parser?

@arobase-che
Copy link
Owner

arobase-che commented Dec 14, 2020 via email

@david-sharer
Copy link

david-sharer commented May 9, 2021

For anyone else who runs into this, I got this working with a custom component for my scenario (I needed call-to-action button-links). All buttons that have <strong> as the first-child [**text here**](... "custom:attribute title text") are rendered as buttons and their title text is parsed for custom attribute strings. Highly limited, but worked for me!
image

This pipeline relies on react-markdown.

// typescript component rendering
import React, { ReactElement } from "react";
import ReactMarkdown from "react-markdown";
import { Components, ReactBaseProps, ReactMarkdownProps } from "react-markdown/src/ast-to-react";
import remarkGfm from "remark-gfm";
import { first, fromPairs } from "lodash";
import { Button } from "@material-ui/core";

const components: Components = {
  a: ({
    node,
    children,
    href,
    title,
    ...props
  }: ReactBaseProps & ReactMarkdownProps & { href: string; title: string }) => {
    // if the first child of the button is a bold element, make this a CTA button
    if ((first(children) as ReactElement)?.type === "strong") {
      const splitTitle = title.split(' ');
      const titleAttrEntries = splitTitle.filter(t => t.includes(':'));
      const remainingTitleEntries = splitTitle.filter(t => !t.includes(':'));
      const finalTitle = remainingTitleEntries.join(' ');
      const titleAttrPairs = titleAttrEntries.map(te => te.split(':'))
      const titleAttrs = fromPairs(titleAttrPairs);
      return (
        <Button variant="contained" href={href} title={finalTitle} {...props} {...titleAttrs}>
          {children}
        </Button>
      );
    }

    return <a href={href} title={title} children={children} {...props}></a>;
  },
};

export function renderMarkdown(markdown: string) {
  return (
    <ReactMarkdown
      plugins={[remarkGfm]}
      children={markdown}
      components={components}/>
  );
}
<!-- markdown -->
[Regular Link](/where_to_go/)

[**CTA Button Link**](/where_to_go/ "color:primary any:attribute things without colons will be treated as the title")

output
image

@david-sharer
Copy link

It looks like this may be a suitable replacement, as well https://github.com/remarkjs/remark-directive

@arobase-che
Copy link
Owner

arobase-che commented May 9, 2021

Directive should have been the way to go since the CommonMark compatibility, but directive never really make it way to the CommonMark standard.

I wonder if it's used anywhere. But in principle, it's nice, maybe a little complex but still nice.

It's also difficult to implement to remark but that not a real argument because attributes-style (like remark-attr) become slightly more complicated to implement with µmark.

PS: If you plan to use remark-directive take a look at https://github.com/micromark/micromark-extension-directive.

@wooorm
Copy link
Author

wooorm commented May 9, 2021

I wonder if it's used anywhere. But in principle, it's nice, maybe a little complex but still nice.

It’s getting some traction: a growing number of questions about it and users of it!

@cah4a
Copy link

cah4a commented Jan 15, 2022

Made a simple plugin from scratch that works well with remark@next:
https://gist.github.com/cah4a/9b75c531540e2891599453863dd24881

Don't know how well it is aligned with the plugins ecosystem. Patching hProperties seems not a very delicate solution, but it works.

If somebody has the power to make a package out of it, feel free to grab the source.

@swamidass
Copy link

Don't know how well it is aligned with the plugins ecosystem. Patching hProperties seems not a very delicate solution, but it works.

If somebody has the power to make a package out of it, feel free to grab the source.

Is this code still working? Has anyone made a package out of it yet? That would be much appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants