Skip to content

Commit

Permalink
Emit diagnostic on malformed fragment.
Browse files Browse the repository at this point in the history
A fragment needs to have a colon between the language
id and the tag. Emitting this warning helps to
quickly identify this type of oversight.

Fixes #31
  • Loading branch information
jesterKing committed Dec 3, 2023
1 parent fdd2f42 commit 3d61bb0
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 9 deletions.
24 changes: 22 additions & 2 deletions literate/literate.html
Original file line number Diff line number Diff line change
Expand Up @@ -1099,14 +1099,15 @@ <h3>Creating and modifying fragments</h3>
<div class="fragmentname">&lt;&lt;fragment regular expressions&gt;&gt;=+</div>
<div class="code">
<pre><code><span class="hljs-keyword">const</span> <span class="hljs-variable constant_">FRAGMENT_RE</span> =
<span class="hljs-regexp">/(?&lt;lang&gt;.*):.*<span class="literate-tag-name">&lt;&lt;(?&lt;tagName&gt;.+)&gt;&gt;</span>(?&lt;root&gt;=)?(?&lt;add&gt;\+)?\s*(?&lt;fileName&gt;.*)/</span>;
<span class="hljs-regexp">/(?&lt;lang&gt;[^:]*)(?&lt;colon&gt;:)?.*<span class="literate-tag-name">&lt;&lt;(?&lt;tagName&gt;.+)&gt;&gt;</span>(?&lt;root&gt;=)?(?&lt;add&gt;\+)?\s*(?&lt;fileName&gt;.*)/</span>;
</code></pre>

</div>
</div><p>Most of the groups correspond to the ones defined by <code>FRAGMENT_USE_IN_CODE_RE</code>
with a few additions. Most notably there is the group catching the language
specifier, and the group to catch the filename, called <code>lang</code> and <code>fileName</code>
respectively.</p>
<p>Also the colon is separated out into a group. That will allow for checking if a tag declaration is properly formed. When the colon is missing it is possible to detect this and emit a diagnostic accordingly.</p>
<p>So to create a new tag the info line for the code fence could look like
<code>py : &lt;&lt;a fragment name&gt;&gt;=</code>.</p>
<p>To add to a fragment a <code>+</code> is added, so it could look like
Expand Down Expand Up @@ -1191,7 +1192,9 @@ <h2>Gathering all fragments</h2>
</div>
</div><p>Each <code>fence</code> token we find we need to check. There may be of course code fences
in the document that do not create or modify a fragment. These we need to skip.</p>
<p>Since we are handling code fences we use <code>FRAGMENT_RE</code> to match <code>token.info</code>.</p>
<p>Since we are handling code fences we use <code>FRAGMENT_RE</code> to match <code>token.info</code>. A
fragment is malformed if the colon is missing, so we need to
<code>&lt;&lt;emit diagnostic when colon is missing&gt;&gt;</code>.</p>
<div class="codefragment">
<div class="fragmentname">&lt;&lt;handle fence tokens&gt;&gt;=</div>
<div class="code">
Expand All @@ -1200,16 +1203,33 @@ <h2>Gathering all fragments</h2>
<span class="hljs-keyword">const</span> match = token.<span class="hljs-property">info</span>.<span class="hljs-title function_">match</span>(<span class="hljs-variable constant_">FRAGMENT_RE</span>);
<span class="hljs-keyword">if</span> (match &amp;&amp; match.<span class="hljs-property">groups</span>) {
<span class="hljs-keyword">let</span> lang = match.<span class="hljs-property">groups</span>.<span class="hljs-property">lang</span>.<span class="hljs-title function_">trim</span>();
<span class="hljs-keyword">let</span> colon = match.<span class="hljs-property">groups</span>.<span class="hljs-property">colon</span>;
<span class="hljs-keyword">let</span> name = match.<span class="hljs-property">groups</span>.<span class="hljs-property">tagName</span>;
<span class="hljs-keyword">let</span> root = match.<span class="hljs-property">groups</span>.<span class="hljs-property">root</span>;
<span class="hljs-keyword">let</span> add = match.<span class="hljs-property">groups</span>.<span class="hljs-property">add</span>;
<span class="hljs-keyword">let</span> fileName = match.<span class="hljs-property">groups</span>.<span class="hljs-property">fileName</span>;
<span class="literate-tag-name">&lt;&lt;emit diagnostic when colon is missing&gt;&gt;</span>
<span class="literate-tag-name">&lt;&lt;add to existing fragment&gt;&gt;</span>
<span class="literate-tag-name">&lt;&lt;create a new fragment&gt;&gt;</span>
}
}
</code></pre>

</div>
</div><h3>Error diagnostic when fragment malformed</h3>
<p>The diagnostic emitted has a message telling the colon is missing, along with
the line number and the literate file this happened in.</p>
<div class="codefragment">
<div class="fragmentname">&lt;&lt;emit diagnostic when colon is missing&gt;&gt;=</div>
<div class="code">
<pre title="<<emit diagnostic when colon is missing>>=">if(lang &amp;&amp; !match.groups.colon) {
let msg = `Missing colon for fragment: ${name}. ${env.literateFileName}${linenumber}`;
const diag = createErrorDiagnostic(token, msg);
updateDiagnostics(env.literateUri, diagnostics, diag);
}

</pre>

</div>
</div><h3>Creating a new fragment</h3>
<p>If the <code>root</code> group has captured a result but not the <code>add</code> group we know we
Expand Down
24 changes: 22 additions & 2 deletions literate/literate.literate
Original file line number Diff line number Diff line change
Expand Up @@ -1045,14 +1045,16 @@ following the language specifier.

``` ts : <<fragment regular expressions>>=+
const FRAGMENT_RE =
/(?<lang>.*):.*<<(?<tagName>.+)>>(?<root>=)?(?<add>\+)?\s*(?<fileName>.*)/;
/(?<lang>[^:]*)(?<colon>:)?.*<<(?<tagName>.+)>>(?<root>=)?(?<add>\+)?\s*(?<fileName>.*)/;
```

Most of the groups correspond to the ones defined by `FRAGMENT_USE_IN_CODE_RE`
with a few additions. Most notably there is the group catching the language
specifier, and the group to catch the filename, called `lang` and `fileName`
respectively.

Also the colon is separated out into a group. That will allow for checking if a tag declaration is properly formed. When the colon is missing it is possible to detect this and emit a diagnostic accordingly.

So to create a new tag the info line for the code fence could look like
`py : <<a fragment name>>=`.

Expand Down Expand Up @@ -1135,24 +1137,42 @@ for (let env of envList) {
Each `fence` token we find we need to check. There may be of course code fences
in the document that do not create or modify a fragment. These we need to skip.

Since we are handling code fences we use `FRAGMENT_RE` to match `token.info`.
Since we are handling code fences we use `FRAGMENT_RE` to match `token.info`. A
fragment is malformed if the colon is missing, so we need to
`<<emit diagnostic when colon is missing>>`.

```ts : <<handle fence tokens>>=
if (token.type === 'fence') {
const linenumber = locationOfFragment(token);
const match = token.info.match(FRAGMENT_RE);
if (match && match.groups) {
let lang = match.groups.lang.trim();
let colon = match.groups.colon;
let name = match.groups.tagName;
let root = match.groups.root;
let add = match.groups.add;
let fileName = match.groups.fileName;
<<emit diagnostic when colon is missing>>
<<add to existing fragment>>
<<create a new fragment>>
}
}
```

### Error diagnostic when fragment malformed

The diagnostic emitted has a message telling the colon is missing, along with
the line number and the literate file this happened in.

```ts: <<emit diagnostic when colon is missing>>=
if(lang && !match.groups.colon) {
let msg = `Missing colon for fragment: ${name}. ${env.literateFileName}${linenumber}`;
const diag = createErrorDiagnostic(token, msg);
updateDiagnostics(env.literateUri, diagnostics, diag);
}

```

### Creating a new fragment

If the `root` group has captured a result but not the `add` group we know we
Expand Down
9 changes: 8 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const emptyToken : TokenUsage =
const FRAGMENT_USE_IN_CODE_RE =
/(?<indent>[ \t]*)<<(?<tagName>.+)>>(?<root>=)?(?<add>\+)?/g;
const FRAGMENT_RE =
/(?<lang>.*):.*<<(?<tagName>.+)>>(?<root>=)?(?<add>\+)?\s*(?<fileName>.*)/;
/(?<lang>[^:]*)(?<colon>:)?.*<<(?<tagName>.+)>>(?<root>=)?(?<add>\+)?\s*(?<fileName>.*)/;
const FRAGMENT_HTML_CLEANUP_RE= /(<span.class="hljs-.+?">)(.*?)(<\/span>)/g;
const FRAGMENT_HTML_RE= /(&lt;&lt;.+?&gt;&gt;)/g;

Expand Down Expand Up @@ -429,10 +429,17 @@ async function handleFragments(
const match = token.info.match(FRAGMENT_RE);
if (match && match.groups) {
let lang = match.groups.lang.trim();
let colon = match.groups.colon;
let name = match.groups.tagName;
let root = match.groups.root;
let add = match.groups.add;
let fileName = match.groups.fileName;
if(lang && !match.groups.colon) {
let msg = `Missing colon for fragment: ${name}. ${env.literateFileName}${linenumber}`;
const diag = createErrorDiagnostic(token, msg);
updateDiagnostics(env.literateUri, diagnostics, diag);
}

if (root && add) {
if (fragments.has(name)) {
let fragmentInfo = fragments.get(name) || undefined;
Expand Down
8 changes: 4 additions & 4 deletions style.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
body {
background-color: antiquewhite;
color: black;
width: 50%;
width: 90%;
margin: auto;
padding-top: 5em;
}
Expand All @@ -19,12 +19,12 @@ div.codefragment {
color: black;
padding-top: 1em;
padding-left: 0.5em;
padding-bottom: 0.1em;
padding-bottom: 0.5em;
}

div.codefragment div+div {
margin-top: 0em;
padding-top: 0em;
margin-top: 0.5em;
padding-top: 0.5em;
color: black;
}

Expand Down

0 comments on commit 3d61bb0

Please sign in to comment.