Skip to content

Commit

Permalink
61 generate list of figures (#68)
Browse files Browse the repository at this point in the history
feat: Generate a List of Figures with option `generateFiles.listOfFigures`. More see README.md. (#61)
  • Loading branch information
about-code authored Jan 19, 2020
1 parent 792253e commit 2c57ace
Show file tree
Hide file tree
Showing 49 changed files with 1,312 additions and 212 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ npm run debug
This starts a remote debug session at `127.0.0.1:9229`. To debug with a configuration in another directory type:

```
npm run debug-cfg -- ./path/to/glossarify-md.conf.json
npm run debug-config -- ./path/to/glossarify-md.conf.json
```

You can now connect e.g. with
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,23 @@ 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.
### List of Figures

> **Since v3.3.0**
```json
"generateFiles": {
"listOfFigures": { "file": "./figures.md", "title": "Abbildungen" }
}
```

This option will generate an index file `./figures.md` with a flat list of figures and links to where they have been used. Optionally you can group figures by page title (depth 1) or sections (depth >= 2).
```json
"indexing": {
"groupByHeadingDepth": 1
}
```

## Options

### `--help` | `--h`
Expand Down
54 changes: 39 additions & 15 deletions conf.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@
},
"generateFiles": {
"description": "File generation options.",
"$ref": "#/$defs/GenerateFiles",
"$ref": "#/$defs/GenerateFilesOpts",
"default": {}
},
"glossaries": {
"description": "An array of glossaries. Allows for different kinds of glossaries and definitions.",
"type": "array",
"items": {
"$ref": "#/$defs/Glossary"
"$ref": "#/$defs/GlossariesOpts"
},
"alias": "g",
"default": [
Expand All @@ -58,23 +58,28 @@
"alias": "i",
"default": false
},
"keepRawFiles": {
"description": "Glob patterns for (markdown) files to copy from 'baseDir' to 'outDir' but to ignore by the linker. Non-markdown files will be ignored anyways.",
"includeFiles": {
"description": "Path or glob patterns of files to include for linking to glossaries.",
"type": "array",
"items": {
"type": "string"
},
"alias": "r",
"default": []
"alias": "f",
"default": ["."]
},
"includeFiles": {
"indexing": {
"description": "Path or glob patterns of files to include for linking to glossaries.",
"$ref": "#/$defs/IndexingOpts",
"default": {}
},
"keepRawFiles": {
"description": "Glob patterns for (markdown) files to copy from 'baseDir' to 'outDir' but to ignore by the linker. Non-markdown files will be ignored anyways.",
"type": "array",
"items": {
"type": "string"
},
"alias": "f",
"default": ["."]
"alias": "r",
"default": []
},
"linking": {
"description": "Whether to use relative or absolute links. Choosing 'absolute' requires a 'baseUrl'.",
Expand Down Expand Up @@ -105,22 +110,29 @@
}
},
"$defs": {
"GenerateFiles": {
"GenerateFilesOpts": {
"type": "object",
"properties": {
"indexFile": {
"description": "Generate a file with a list of glossary terms and where they have been used.",
"oneOf": [{
"type": "string"
}, {
"type": "object",
"$ref": "#/$defs/IndexFile"
"$ref": "#/$defs/IndexFileOpts"
},{
"type": "string",
"description": "String value range is deprecated and will be removed with v4. Use object notation instead."
}],
"default": ""
},
"listOfFigures": {
"description": "Generate a file with a list of figures and where they can be found.",
"type": "object",
"$ref": "#/$defs/IndexFileOpts",
"default": ""
}
}
},
"IndexFile": {
"IndexFileOpts": {
"type": "object",
"properties": {
"file": {
Expand All @@ -133,7 +145,19 @@
}
}
},
"Glossary": {
"IndexingOpts": {
"type": "object",
"properties": {
"groupByHeadingDepth": {
"description": "Level of detail by which to group occurrences of terms or syntactic elements in generated files (Range [min; max] = [0; 9]). For example, use 0 to not group at all; 1 to group things at the level of document titles, etc. Configures the indexer. The option affects any files generated from the internal AST node index.",
"type": "number",
"minimum": 0,
"maximum": 9,
"default": 0
}
}
},
"GlossariesOpts": {
"type": "object",
"properties": {
"file": {
Expand Down
1 change: 0 additions & 1 deletion lib/ast/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ api.getNodeText = function getNodeText(node) {
} else {
return;
}

};

/**
Expand Down
120 changes: 120 additions & 0 deletions lib/figures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
const {root, paragraph, text, heading, brk, link, list, listItem } = require("mdast-builder");

const {getFileLinkUrl} = require("./path/tools");
const {getLinkUrl, getNodeText} = require("./ast/tools");
const {getNodeIndex, getDefinitionIndex, groupByHeading} = require("./indexer");

const api = {};

/**
* @typedef { import('./model/context') } Context
* @typedef { import('./indexer').IndexEntry } IndexEntry
* @typedef { import('./indexer').Index } Index
*/

/**
* Returns the markdown abstract syntax tree that is to be written to the file
* configured via 'generateFiles.indexFile' config.
*
* @param {Context} context
* @returns {Node} mdast tree
*/
api.getAST = function(context) {
const {generateFiles, indexing} = context.opts;
const {groupByHeadingDepth} = indexing;
const {title} = generateFiles.listOfFigures;
const figures = selectFiguresFromIndex(context);

let tree = [
heading(1, text(title || "Figures"))
];
if (! groupByHeadingDepth || groupByHeadingDepth < 0) {
tree.push(getListOfFiguresAst(context, figures));
} else {
tree.push(getFiguresBySectionAst(context, figures));
}
return root(tree);
};

/**
* @param {Context} context
* @param {IndexEntry[]} figures
*/
function getFiguresBySectionAst(context, figures) {
return paragraph(
groupByHeading(figures).map((figures) => {
const groupHeadingNode = figures[0].groupHeadingNode;
return paragraph([
// add +1 to depth of headings referred to in order to keep
// the title of the generated file the only depth-1 heading
brk
,heading(groupHeadingNode.depth + 1, text(getNodeText(groupHeadingNode)))
,brk
,brk
,getListOfFiguresAst(context, figures)
,brk
]);
})
);
}

/**
* @param {Context} context
* @param {IndexEntry[]} figures
* @returns {Node} mdast tree
*/
function getListOfFiguresAst(context, figures) {
return list(
"ordered"
,figures
.sort((entry1, entry2) => entry1.id - entry2.id)
.map((indexEntry) => getListOfFiguresItemAst(context, indexEntry))
);
}

/**
* @param {Context} context
* @param {IndexEntry} indexEntry
* @returns {Node} mdast tree
*/
function getListOfFiguresItemAst(context, indexEntry) {
const {listOfFigures} = context.opts.generateFiles;
const {file: listOfFiguresFile} = listOfFigures;

return listItem([
link(
getFileLinkUrl(context, listOfFiguresFile, indexEntry.file, getLinkUrl(indexEntry.headingNode))
,indexEntry.node.title
,text(indexEntry.node.alt)
)
]);
}

/**
* @returns {IndexEntry[]} index entries for node types "image" and "imageReference"
*/
function selectFiguresFromIndex() {
const images = getNodeIndex("image");
const imageRefs = getNodeIndex("imageReference");
const defIndex = getDefinitionIndex();
let figures = [
...images
,...imageRefs.map((indexEntry) => {
// dereference
const refFile = indexEntry.file;
const refNode = indexEntry.node;
const defId = `${refFile}#${refNode.identifier}`;
const defNode = defIndex[defId].node;
indexEntry.node = {
...indexEntry.node
, type: "image"
, url: defNode.url
, title : defNode.title
};
return indexEntry;
})
];
return figures;
}

module.exports = api;
Loading

0 comments on commit 2c57ace

Please sign in to comment.