Draft of test-code-document

- generic hexo files
 - scripts for footnotes and creating captions.
This commit is contained in:
Daniel Lopretto 2020-08-30 20:11:27 -04:00
parent 4c47c9e5e2
commit c0e2f31665
11 changed files with 1534 additions and 11 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ node_modules/
public/
.deploy*/
source/about/index.md
themes/

View File

@ -97,6 +97,9 @@ ignore:
# Extensions
## Plugins: https://hexo.io/plugins/
plugins:
- hexo-reference
## Themes: https://hexo.io/themes/
theme: cactus

View File

@ -3,8 +3,8 @@
"version": "0.0.0",
"private": true,
"scripts": {
"prebuild": "node scripts/mkabout.js",
"build": "hexo generate",
"debug": "node --inspect-brk /Users/dan/.config/yarn/global/node_modules/.bin/hexo generate",
"clean": "hexo clean",
"deploy": "hexo deploy",
"server": "hexo server"

4
scaffolds/draft.md Normal file
View File

@ -0,0 +1,4 @@
---
title: {{ title }}
tags:
---

4
scaffolds/page.md Normal file
View File

@ -0,0 +1,4 @@
---
title: {{ title }}
date: {{ date }}
---

5
scaffolds/post.md Normal file
View File

@ -0,0 +1,5 @@
---
title: {{ title }}
date: {{ date }}
tags:
---

17
scripts/create-caption.js Normal file
View File

@ -0,0 +1,17 @@
const reMatchBlocks = /^(\(#[^)]+\))\s*(.*)[\r\n]+(<hexoPostRenderCodeBlock><figure[^>]*>)(.*?)(<\/figure><\/hexoPostRenderCodeBlock>)/gm;
hexo.extend.filter.register('before_post_render', function augmentFigures(data) {
data.content = data.content.replace(reMatchBlocks,
(match, directive, caption, start, table ,end) => {
let captionHtml = '';
let directiveHtml = ' <figcaption class="directive">' + directive + '</figcaption>\n'
if (caption) {
captionHtml += ' <figcaption class="caption">' + caption + '</figcaption>\n';
}
return start + captionHtml + table + directiveHtml + end;
}
);
return data;
}, 10);

89
scripts/footnotes.js Normal file
View File

@ -0,0 +1,89 @@
/**
* @link https://github.com/kchen0x/hexo-reference
*
* Render markdown footnotes
* @param {String} text
* @returns {String} text
*/
function renderFootnotes(text) {
var footnotes = [];
var reFootnoteContent = /\[\^(\d+)\]: ?([\S\s]+?)(?=\[\^(?:\d+)\]|\n\n|$)/g;
var reInlineFootnote = /\[\^(\d+)\]\((.+?)\)/g;
var reFootnoteIndex = /\[\^(\d+)\]/g;
var html = '';
// threat all inline footnotes
text = text.replace(reInlineFootnote, function (match, index, content) {
footnotes.push({
index: index,
content: content
});
// remove content of inline footnote
return '[^' + index + ']';
});
// threat all footnote contents
text = text.replace(reFootnoteContent, function (match, index, content) {
footnotes.push({
index: index,
content: content
});
// remove footnote content
return '';
});
// create map for looking footnotes array
function createLookMap(field) {
var map = {}
for (var i = 0; i < footnotes.length; i++) {
var item = footnotes[i]
var key = item[field]
map[key] = item
}
return map
}
var indexMap = createLookMap("index")
// render (HTML) footnotes reference
text = text.replace(reFootnoteIndex,
function(match, index){
var tooltip = indexMap[index].content;
return '<sup id="fnref:' + index + '">' +
'<a href="#fn:'+ index +'" rel="footnote">' +
'<span class="" aria-label="'
+ tooltip +
'">[' + index +']</span></a></sup>';
});
// sort footnotes by their index
footnotes.sort(function (a, b) {
return a.index - b.index;
});
// render footnotes (HTML)
footnotes.forEach(function (footNote) {
html += '<li id="fn:' + footNote.index + '">';
html += '<span style="display: inline-block; vertical-align: top; padding-right: 10px; margin-left: -40px">';
html += footNote.index;
html += '.</span>';
html += '<span style="display: inline-block; vertical-align: top; margin-left: 10px;">';
html += footNote.content.trim();
html += '<a href="#fnref:' + footNote.index + '" rev="footnote"> &hookleftarrow;</a></span></li>';
});
// add footnotes at the end of the content
if (footnotes.length) {
text += '<div id="footnotes">';
text += '<hr>';
text += '<div id="footnotelist">';
text += '<ol style="list-style: none; padding-left: 0; margin-left: 40px">' + html + '</ol>';
text += '</div></div>';
}
return text;
}
// Register footnotes filter
hexo.extend.filter.register('before_post_render', function(data) {
data.content = renderFootnotes(data.content);
return data;
});

View File

@ -0,0 +1,38 @@
---
title: Hello World
---
Welcome to [Hexo](https://hexo.io/)! This is your very first post. Check [documentation](https://hexo.io/docs/) for more info. If you get any problems when using Hexo, you can find the answer in [troubleshooting](https://hexo.io/docs/troubleshooting.html) or you can ask me on [GitHub](https://github.com/hexojs/hexo/issues).
## Quick Start
### Create a new post
``` bash
$ hexo new "My New Post"
```
More info: [Writing](https://hexo.io/docs/writing.html)
### Run server
``` bash
$ hexo server
```
More info: [Server](https://hexo.io/docs/server.html)
### Generate static files
``` bash
$ hexo generate
```
More info: [Generating](https://hexo.io/docs/generating.html)
### Deploy to remote sites
``` bash
$ hexo deploy
```
More info: [Deployment](https://hexo.io/docs/one-command-deployment.html)

View File

@ -23,9 +23,9 @@ The main project I worked on was safety critical black-box/white-box testing. Re
the min safe value is return instead.
```
It doesn't take a sophisticated parser to turn the above into an Object Model. Faced with the same task now I would modify the syntax slightly and use a YAML or Markdown parser<sup>[1](#Notes)</sup>.
It doesn't take a sophisticated parser to turn the above into an Object Model. Faced with the same task now I would modify the syntax slightly and use a YAML or Markdown parser[^1].
The test harness would take a similarly outlined document and generate the required C source code and header files. Specific unit tests had setup, tear down, and test code blocks<sup>[2](#Notes)</sup>.
The test harness would take a similarly outlined document and generate the required C source code and header files. Specific unit tests had setup, tear down, and test code blocks[^2].
```
- Covers: SRS-0123
@ -63,7 +63,7 @@ When everything you write can be parsed, computed, and generated into a new form
*I've written 3 API documents for third party developers in the last week.*
Three outside companies (integrators) want to interface with our system and need instruction on how to do so. The sticky bit is that we have different NDAs and SLAs with each of these integrators. Also, the bulk of our API is outside of the scope of what we want to share. Some of it is protected intellectual property, some is custom for just one integrator<sup>[3](#Notes)</sup>.
Three outside companies (integrators) want to interface with our system and need instruction on how to do so. The sticky bit is that we have different NDAs and SLAs with each of these integrators. Also, the bulk of our API is outside of the scope of what we want to share. Some of it is protected intellectual property, some is custom for just one integrator[^3].
There was duplication of effort in building these documents. An outline of a doc follows:
@ -83,7 +83,7 @@ There was duplication of effort in building these documents. An outline of a doc
- Example request
- Example response
Of the six top level bullet points three (*1, 3, 5*) are the same in all documents. The *change history* is document specific; *Common errors* are the same in meaning, across documents, but may have different forms (XML<sup>[4](#Notes)</sup> or JSON); *Endpoint descriptions* contain the meat of the differences and the minimum of procedural generation must allow customizing these pages in each document.
Of the six top level bullet points three (*1, 3, 5*) are the same in all documents. The *change history* is document specific; *Common errors* are the same in meaning, across documents, but may have different forms (XML[^4] or JSON); *Endpoint descriptions* contain the meat of the differences and the minimum of procedural generation must allow customizing these pages in each document.
## Inspirations
@ -144,11 +144,145 @@ The returned `tokens` is an array of [`ContentToken`](https://github.com/Definit
Let's look at that `markdownParser` code block again. There are two distinct parts: (a) the `require` statements and (b) a function definition. If we stripped out the all the code blocks in this file and concatenated them together we would have a running script, but it would not be well organized. The order that you describe things is not always the order in which the compiler/interpreter wants them. This is fundamental in Donald Knuth's description of the Weave and Tangle programs.
For now we have used simple names for the blocks: `Function`, `Usage`, `markdownParser`.
For now we have used simple names for the blocks: `Function`, `Usage`, `markdownParser`. We can standardize these into a syntax, one that won't collide with other markdown syntax and allows us to: (1) signal that this is no ordinary code block, we need to do something with it, (2) a verb that describes what to do, (3) a noun that can act as a label or destination, and (4) the purpose of the code block, its *raison d'être*.
```
(#save: "somefile.js":imports)
(#save: "somefile.js":function-definitions) Definition of markdownParser
```
That parses into:
- verb: save
- label: imports
- purpose: somefile.js
## Notes
- [1] Maybe not ... our parser was so minimal that the self-test for it was easy to write/maintain.
- [2] A boon of this test harness that I have not seen in other package was the ability to turn off code coverage in the setup and tear down code blocks. Coverage was only recorded for actual testing.
- [3] a moment of weakness
- [4] My inclusion of XML is not an endorsement of the technology.
I added some descriptive text to line two. That's an optional caption for the rendered code block.
Markdown standards don't have or reserve `(#...)` sequences. In general a parenthetical does not denote a behavior unless following `[]` for anchors, or `![]` for images. An octothorpe[^5] is equally meaningless; however, it is an escapable character.
(#save: parser.js:constants) Regular Expression to capture commands
```javascript
const reCmdCapture = /^\(#(?<cmd>[a-z][a-z_-]+)\s*:\s*["'`](?<fn>[^["'`]+)["'`]\s*:?\s*(?<label>[^\)]*)\)\s*(?<caption>[^\n]*)/;
function captureCommand(str) {
const matches = str.match(reCmdCapture)
return matches
? {
command: matches.groups.cmd,
purpose: matches.groups.purp,
label: matches.groups.label,
caption: matches.groups.caption
}
: false;
}
```
That's one way to do it...so is:
(#save: parser.js:constants) Regular Expression to match commands
```javascript
const reCmdMatcher = /^\(#[^)]+\)\s+[^\n]*$/;
function matchCommand(str) {
const matches = str.match(reCmdMatcher);
if (!!matches) {
let [directive, caption] = str.split(/\)\s*/);
let [command, purpose, label] = directive.substr(2).split(/\s*:\s*/);
purpose = purpose.replace(/['"`]/g, '');
return { command, purpose, label, caption };
}
return false;
}
```
(#save: test-parser.js:test) Test matcher and capture options.
```javascript
const { expect } = require('chai');
describe('Parsing', () => {
const samples = [
'(#save: "somefile.js":imports)',
'(#save: "somefile.js":function-definitions) Definition of markdownParser',
'(#save: parser.js:constants) Regular Expression to capture commands',
'Will not match',
'Also, not a match: (#save: "somefile.js":imports)'
];
describe('Capture Style', () => {
it('finds no captions', () => {
expect(captureCommand(samples[0])).to.eql({
command: 'save',
purpose: 'somefile.js',
label: 'imports',
caption: ''
});
});
it('finds with captions', () => {
expect(captureCommand(samples[1])).to.eql({
command: 'save',
purpose: 'somefile.js',
label: 'function-definitions',
caption: 'Definition of markdownParser'
});
});
it('finds, optional quotes around purpose/filename', () => {
expect(captureCommand(samples[2])).to.eql({
command: 'save',
purpose: 'parser.js',
label: 'constants',
caption: 'Regular Expression to capture commands'
});
});
it('returns false if not match', () => {
expect(captureCommand(samples[3])).to.eql(false);
});
it('ignores directives that are not in the first column', () => {
expect(captureCommand(samples[4])).to.eql(false);
});
});
describe('Matcher Style', () => {
it('finds no captions', () => {
expect(matchCommand(samples[0])).to.eql({
command: 'save',
purpose: 'somefile.js',
label: 'imports',
caption: ''
});
});
it('finds with captions', () => {
expect(matchCommand(samples[1])).to.eql({
command: 'save',
purpose: 'somefile.js',
label: 'function-definitions',
caption: 'Definition of markdownParser'
});
});
it('finds, optional quotes around purpose/filename', () => {
expect(matchCommand(samples[2])).to.eql({
command: 'save',
purpose: 'parser.js',
label: 'constants',
caption: 'Regular Expression to capture commands'
});
});
it('returns false if not match', () => {
expect(matchCommand(samples[3])).to.eql(false);
});
it('ignores directives that are not in the first column', () => {
expect(matchCommand(samples[4])).to.eql(false);
});
});
});
```
[^1]: Maybe not ... our parser was so minimal that the self-test for it was easy to write/maintain.
[^2]: A boon of this test harness that I have not seen in other package was the ability to turn off code coverage in the setup and tear down code blocks. Coverage was only recorded for actual testing.
[^3]: A moment of weakness
[^4]: My inclusion of XML is not an endorsement of the technology.
[^5]: Vim's default English dictionary doesn't include this common term for a hash mark. The suggested replacement is `ectotherm`.

1228
yarn.lock Normal file

File diff suppressed because it is too large Load Diff