Skip to content

Commit

Permalink
55 only last occurrence of a shared term gets linked with all its def…
Browse files Browse the repository at this point in the history
…initions (#56)

fix: All occurrences of an ambiguously defined term will be linked with all definitions, not just the last occurrence. Closes #55.
  • Loading branch information
about-code authored Dec 24, 2019
1 parent 62e762c commit c891c87
Show file tree
Hide file tree
Showing 32 changed files with 603 additions and 407 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,6 @@ This option will generate a file `./book-index.md` with a list of glossary terms

> **Note**: If you plan translating markdown to HTML, e.g. with [vuepress](https://vuepress.vuejs.org), be aware that a file `index.md` will translate to `index.html` which is typically reserved for the default HTML file served under a domain. You may want to choose another name.
> **Note (vuepress)**: A page title "Index" may not appear in the [vuepress](https://vuepress.vuejs.org) sidebar.
## Options

### `--help` | `--h`
Expand Down
4 changes: 2 additions & 2 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const minimist = require("minimist");
const fs = require("fs");
const path = require("path");
const proc = require("process");
const glossarify = require("../lib/glossarify");
const program = require("../lib/main");
const confSchema = require("../conf.schema.json").properties;
const messages = require("../lib/messages");
const {version} = require("../package.json");
Expand Down Expand Up @@ -63,7 +63,7 @@ opts.outDir = path.resolve(opts.baseDir, opts.outDir);
validateOpts(opts);

//_/ Run \______________________________________________________________________
glossarify.glossarify(opts);
program.run(opts);

//_/ Helpers \__________________________________________________________________
function validateOpts(conf) {
Expand Down
11 changes: 8 additions & 3 deletions lib/ast-tools.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
const path = require("path");
const api = {};

/**
* @typedef {{ type: string, children: Node[] }} Node
*/

/**
* Return the text value of a node. Traverses a node's first-child path down to
* the text node and returns the text node's value. Returns `undefined` if
Expand Down Expand Up @@ -44,8 +48,9 @@ api.noopCompiler = function noopCompiler() {
* Helper function for debugging purposes.
* @private
*/
api.printAst = function printAst(regExpOrBool) {
return () => (tree, file) => {
api.printAst = function (opts) {
const regExpOrBool = opts.match;
return (tree, file) => {
if (
(typeof regExpOrBool === "boolean" && regExpOrBool === true) ||
(typeof regExpOrBool === "string" &&
Expand All @@ -59,7 +64,7 @@ ${JSON.stringify(tree, null, 4)}
`)
}
return tree;
}
};
};


Expand Down
34 changes: 34 additions & 0 deletions lib/context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const Dictionary = require("./dictionary");
const Glossary = require("./glossary");

class Context {

/**
* @param {{ opts:{[key: string]: any} }} data
*/
constructor(data) {
this.opts = data.opts;
this.terms = new Dictionary();

/** @type {VFile} */
this.vFiles = [];

/** @type {{ [path: string]: Glossary}} */
this.glossaries = {};

/**
* Resolved absolute base directory path. Use this for processing files
* since `opts.baseDir` is only the unresolved configuration value.
*/
this.baseDir = "";

/**
* Resolved absolute output directory path. Use this for processing
* files since `opts.baseDir` is only the unresolved configuration
* value.
*/
this.outDir = "";
}
}

module.exports = Context;
10 changes: 7 additions & 3 deletions lib/counter.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
const uVisit = require('unist-util-visit');
const {getLinkUrl} = require('./ast-tools');

const api = {};

/**
* Unified plug-in to count occurrences and mentions of a term.
* Won't count occurrences in the terms own definition.
*
*/
function counter(context) {
return () => (tree, vFile) => {
api.counter = function() {
return (tree, vFile) => {
uVisit(tree, 'term-occurrence', (node, idx, parent) => {
node.termDefs.forEach(term => {
if (vFile.path === term.glossary.vFile.path) {
Expand All @@ -25,4 +29,4 @@ function counter(context) {
};
}

module.exports = { counter };
module.exports = api;
59 changes: 40 additions & 19 deletions lib/terminator.js → lib/glossarifier.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
const {printAst, noopCompiler, getNodeText} = require("./ast-tools.js");
const Term = require("./term.js");
const {toForwardSlash} = require("./pathplus");
const path = require("path");
const unified = require("unified");
const unifiedNgin = require("unified-engine");
const uVisit = require("unist-util-visit");
const remark_parse = require("remark-parse");
const remark_slug = require("remark-slug");
const remark_stringify = require("remark-stringify");
const remark_link_headings = require("remark-autolink-headings");

const Context = require("./context");
const Glossary = require("./glossary");
const Term = require("./term");
const {printAst, noopCompiler, getNodeText} = require("./ast-tools");
const {toForwardSlash} = require("./pathplus");

const api = {};

/**
* @private
* Reads glossary markdown files into a dictionary.
*
* @param {Context} context
* @return {Promise<Term[]>} terms
* @return {Promise<Context>} context
*/
api.readTermDefinitions = function(context) {
api.readGlossaries = function(context) {
prepare(context);
const {
baseDir, outDir, glossaries, keepRawFiles, excludeFiles,
experimentalFootnotes
experimentalFootnotes, dev
} = context.opts;
return new Promise((resolve, reject) => {
unifiedNgin(
{
processor: unified()
.use(remark_parse, { footnotes: experimentalFootnotes })
.use(printAst(context.opts.dev.printInputAst)) // Might be regex. /.*\/table\.md/g;
.use(printAst, { match: dev.printInputAst }) // Might be regex. /.*\/table\.md/g;
.use(remark_slug)
.use(remark_link_headings, {behavior: 'wrap'})
.use(terminator(context))
.use(printAst(context.opts.dev.printOutputAst)) // Might be regex. /.*\/table\.md/g;
.use(terminator, { context: context })
.use(printAst, { match: dev.printOutputAst }) // Might be regex. /.*\/table\.md/g;
.use(noopCompiler)
.use(remark_stringify)
,cwd: baseDir
,files: toForwardSlash(glossaries.map(g => g.file).sort((f1,f2) => f1.localeCompare(f2, 'en')))
,ignoreName: '.mdignore'
Expand All @@ -49,18 +52,35 @@ api.readTermDefinitions = function(context) {
,color: true
,silent: false
},
(err, statusCode, uContext) => {
(err) => {
if(err) {
reject(err);
} else {
//context.vFiles = [...context.vFiles, ...uContext.files];
resolve(context);
}
}
)
});
}

function prepare(context) {
const {glossaries, baseDir, outDir} = context.opts;

// Instantiate `context.opts.glossaries` into an array of type `Glossary[]`.
// Thereby set up a `context.glossaries` map with keys being a glossary
// file path and value being a `Glossary` instance in order to access
// a `Glossary` instance by path. The latter allows to look up the glossary
// config by vFile metadata when proccesing glossary files with unifiedjs.
context.opts.glossaries = glossaries.map(conf => {
const g = new Glossary(conf);
g.file = conf.file || path.join(baseDir, "glossary.md");
g.basePath = toForwardSlash(path.resolve(baseDir, conf.file));
g.outPath = toForwardSlash(path.resolve(outDir, conf.file));
context.glossaries[g.basePath] = g;
return g;
});
}

/**
* Terminator climbs along the shallow markdown syntax tree
* where he picks up all those terms humans use to describe
Expand All @@ -70,10 +90,11 @@ api.readTermDefinitions = function(context) {
* @private
* @param {*} context
*/
function terminator(context) {
const { glossaries, opts } = context;
const { baseDir } = opts;
return () => (tree, file) => {
function terminator(opts) {
const { context } = opts;
const { glossaries } = context;
const { baseDir } = context.opts;
return (tree, file) => {
const glossaryKey = toForwardSlash(path.resolve(baseDir, file.path));
const glossary = glossaries[glossaryKey];
if (! glossary) {
Expand Down Expand Up @@ -104,7 +125,7 @@ State.next = (nextState) => State.active = nextState;
* @param {*} glossary
*/
function getTermVisitor(context, glossary) {
return function visitor(node, index, parent) {
return function visitor(node, idx, parent) {
const {terms, opts} = context;
if (node.type === "heading") {
if (node.depth === 1) {
Expand Down
26 changes: 24 additions & 2 deletions lib/glossary.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
const {toReproducablePath} = require("./pathplus");

class Glossary {

/**
* @param {Partial<Glossary>} data
*/
constructor (data) {

/** @type {string} */
this.title = data.title || "";

/** @type {string} */
this.file = data.file || "";

/** @type {VFile} */
this.vFile = data.vFile || null;

/** @type {string} */
this.termHint = data.termHint || "";

/** @type {string} */
this.basePath = data.basePath || "";

/** @type {string} */
this.outPath = data.outPath || "";
}

/**
* @private
*/
toJSON() {
return {
file: this.file,
termHint: this.termHint,
basePath: this.basePath,
outPath: this.outPath,
basePath: toReproducablePath(this.basePath, "/{CWD}"),
outPath: toReproducablePath(this.outPath, "/{CWD}"),
};
}
}
Expand Down
Loading

0 comments on commit c891c87

Please sign in to comment.