Refactor markdown headings parsing

This commit is contained in:
Roman Hotsiy 2017-01-30 17:21:12 +02:00
parent 1ff87ce665
commit 7fa850b41d
No known key found for this signature in database
GPG Key ID: 5CB7B3ACABA57CB0
3 changed files with 43 additions and 32 deletions

View File

@ -8,7 +8,7 @@ import { SpecManager } from '../utils/spec-manager';
import { SchemaHelper } from './schema-helper.service'; import { SchemaHelper } from './schema-helper.service';
import { AppStateService } from './app-state.service'; import { AppStateService } from './app-state.service';
import { LazyTasksService } from '../shared/components/LazyFor/lazy-for'; import { LazyTasksService } from '../shared/components/LazyFor/lazy-for';
import { JsonPointer } from '../utils/JsonPointer'; import { JsonPointer, MarkdownHeading, StringMap } from '../utils/';
import * as slugify from 'slugify'; import * as slugify from 'slugify';
@ -250,40 +250,35 @@ export class MenuService {
addMarkdownItems() { addMarkdownItems() {
let schema = this.specMgr.schema; let schema = this.specMgr.schema;
for (let header of (<Array<string>>(schema.info && schema.info['x-redoc-markdown-headers'] || []))) { let headings = schema.info['x-redoc-markdown-headers'] as StringMap<MarkdownHeading>;
let id = 'section/' + slugify(header); Object.keys(headings).forEach(h => {
let heading = headings[h];
let id = 'section/' + heading.id;
let item = { let item = {
name: header, name: heading.title,
id: id, id: id,
items: null items: null
}; };
item.items = this.getMarkdownSubheaders(item); item.items = this.getMarkdownSubheaders(item, heading);
this.items.push(item); this.items.push(item);
} });
} }
getMarkdownSubheaders(parent: MenuItem):MenuItem[] { getMarkdownSubheaders(parent: MenuItem, parentHeading: MarkdownHeading):MenuItem[] {
let res = []; let res = [];
let schema = this.specMgr.schema; Object.keys(parentHeading.children || {}).forEach(h => {
for (let subheader of (<Array<string>>(schema.info && schema.info['x-redoc-markdown-subheaders'] || []))) { let heading = parentHeading.children[h];
let parts = subheader.split('/'); let id = 'section/' + heading.id;
let header = parts[0];
if (parent.name !== header) {
continue;
}
let name = parts[1];
let id = parent.id + '/' + slugify(name);
let subItem = { let subItem = {
name: name, name: heading.title,
id: id, id: id,
parent: parent parent: parent
}; };
res.push(subItem); res.push(subItem);
} });
return res; return res;
} }

View File

@ -3,6 +3,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import * as slugify from 'slugify'; import * as slugify from 'slugify';
import * as Remarkable from 'remarkable'; import * as Remarkable from 'remarkable';
import { StringMap } from './';
declare var Prism: any; declare var Prism: any;
const md = new Remarkable({ const md = new Remarkable({
@ -19,11 +20,17 @@ const md = new Remarkable({
} }
}); });
export interface MarkdownHeading {
title?: string;
id: string;
content?: string;
children?: StringMap<MarkdownHeading>;
}
@Injectable() @Injectable()
export class MdRenderer { export class MdRenderer {
public firstLevelHeadings: string[] = []; public headings: StringMap<MarkdownHeading> = {};
public secondLevelHeadings: string[] = []; currentTopHeading: MarkdownHeading;
public currentHeading: string = null;
private _origRules:any = {}; private _origRules:any = {};
private _preProcessors:Function[] = []; private _preProcessors:Function[] = [];
@ -45,21 +52,31 @@ export class MdRenderer {
md.renderer.rules.heading_close = this._origRules.close; md.renderer.rules.heading_close = this._origRules.close;
} }
saveHeading(title: string, parent:MarkdownHeading = {id:null, children: this.headings}) :MarkdownHeading {
let id = slugify(title);
if (parent && parent.id) id = `${parent.id}/${id}`;
parent.children = parent.children || {};
parent.children[id] = {
title,
id
};
return parent.children[id];
}
headingOpenRule(tokens, idx) { headingOpenRule(tokens, idx) {
if (tokens[idx].hLevel > 2 ) { if (tokens[idx].hLevel > 2 ) {
return this._origRules.open(tokens, idx); return this._origRules.open(tokens, idx);
} else { } else {
let content = tokens[idx + 1].content; let content = tokens[idx + 1].content;
if (tokens[idx].hLevel === 1 ) { if (tokens[idx].hLevel === 1 ) {
this.firstLevelHeadings.push(content); this.currentTopHeading = this.saveHeading(content);;
this.currentHeading = content; let id = this.currentTopHeading.id;
let contentSlug = slugify(content); return `<h${tokens[idx].hLevel} section="section/${id}">` +
return `<h${tokens[idx].hLevel} section="section/${contentSlug}">` + `<a class="share-link" href="#section/${id}"></a>`;
`<a class="share-link" href="#section/${contentSlug}"></a>`;
} else if (tokens[idx].hLevel === 2 ) { } else if (tokens[idx].hLevel === 2 ) {
this.secondLevelHeadings.push(this.currentHeading + `/` + content); let heading = this.saveHeading(content, this.currentTopHeading);
let contentSlug = slugify(this.currentHeading) + `/` + slugify(content); let contentSlug = `${heading.id}`;
return `<h${tokens[idx].hLevel} section="section/${contentSlug}">` + return `<h${tokens[idx].hLevel} section="section/${heading.id}">` +
`<a class="share-link" href="#section/${contentSlug}"></a>`; `<a class="share-link" href="#section/${contentSlug}"></a>`;
} }
} }

View File

@ -84,8 +84,7 @@ export class SpecManager {
mdRender.addPreprocessor(SecurityDefinitions.insertTagIntoDescription); mdRender.addPreprocessor(SecurityDefinitions.insertTagIntoDescription);
} }
this._schema.info['x-redoc-html-description'] = mdRender.renderMd(this._schema.info.description); this._schema.info['x-redoc-html-description'] = mdRender.renderMd(this._schema.info.description);
this._schema.info['x-redoc-markdown-headers'] = mdRender.firstLevelHeadings; this._schema.info['x-redoc-markdown-headers'] = mdRender.headings;
this._schema.info['x-redoc-markdown-subheaders'] = mdRender.secondLevelHeadings;
} }
get schema() { get schema() {