arve0 / markdown-it-attrs

Add classes, identifiers and attributes to your markdown with {} curly brackets, similar to pandoc's header attributes
MIT License
300 stars 58 forks source link

conflict with closing brace in attribute value #154

Open Netsaver opened 3 months ago

Netsaver commented 3 months ago

Markdown-it versions: not available (maybe 2021)

Example input:

this is the markdown I'm trying to parse {.replace-me data-tex="e^{i\pi}=-1"}

Current output (e.g. RunKit):

<p>this is the markdown I'm trying to parse {.replace-me data-tex="e^{i\pi}=-1"}</p>

Expected output:

<p class="replace-me" data-tex="e^{i\pi}=-1">this is the markdown I'm trying to parse</p>

As additional info, in some application environment (TiddlyWiki) I got:

<p class="replace-me" data-tex="e^{i\pi">this is the markdown I'm trying to parse}=-1</p>

So at the end it seems that the closing brace interferes with the md opening one. I imagine that escaping them in the user field could be a solution, but often there is no direct access to field value as it could be auto-generated, or the escaping of all braces could be time-consuming.

Another solution could be change the delimiters to other parenthesis, but again I would suspect that this could interfere with value content or other md constructs/plugins...

Finally, inspecting the code I found something like HTML_REPLACEMENTS, maybe (but I don't know if it is correct) the set of special chars could be extended to include other conflicting chars for a sort of automatic escaping? So it would be possible to escape braces in the attribute values automatically from the code?

Thanks and regards

arve0 commented 3 months ago

Hi! Thanks for the report. I'm able to reproduce this bug. Until it's fixed you can use custom delimiters.

arve0 commented 3 months ago

Bug is in utils.js:182-187:

    case 'end':
      // last char should be }
      start = str.lastIndexOf(options.leftDelimiter);
      end = start === -1 ? -1 : str.indexOf(options.rightDelimiter, start + rightDelimiterMinimumShift);
      end = end === str.length - options.rightDelimiter.length ? end : -1;
      break;

In above code, str.lastIndexOf(options.leftDelimiter) will be { inside attributes, not outside.

test-case.js:

const utils = require('./utils');

const hasDelimitersInEnd = utils.hasDelimiters('end', { leftDelimiter: '{', rightDelimiter: '}' });
const src = `text {with="attrs {inside}"}`;

const res = hasDelimitersInEnd(src);
console.log(res);
Netsaver commented 3 months ago

Fine thanks... I'll look for an update of this library, I found it very useful! 👍