Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Category improvements #938

Merged
merged 4 commits into from
Mar 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 116 additions & 66 deletions src/lib/converter/plugins/CategoryPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Reflection, ContainerReflection } from '../../models/reflections/index';
import { Reflection, ContainerReflection, DeclarationReflection } from '../../models';
import { ReflectionCategory } from '../../models/ReflectionCategory';
import { SourceDirectory } from '../../models/sources/directory';
import { Component, ConverterComponent } from '../components';
import { Converter } from '../converter';
import { Context } from '../context';
import { GroupPlugin } from './GroupPlugin';
import { Option } from '../../utils/component';
import { ParameterType } from '../../utils/options/declaration';
import { Comment } from '../../models/comments/index';

/**
* A handler that sorts and categorizes the found reflections in the resolving phase.
Expand All @@ -13,19 +14,57 @@ import { GroupPlugin } from './GroupPlugin';
*/
@Component({name: 'category'})
export class CategoryPlugin extends ConverterComponent {
/**
* Define the sort order of categories. By default, sort alphabetically.
*/
@Option({
name: 'defaultCategory',
help: 'Specifies the default category for reflections without a category.',
type: ParameterType.String,
defaultValue: 'Other'
})
defaultCategory!: string;

@Option({
name: 'categoryOrder',
help: 'Specifies the order in which categories appear. * indicates the relative order for categories not in the list.',
type: ParameterType.Array
})
categoryOrder!: string[];

@Option({
name: 'categorizeByGroup',
help: 'Specifies whether categorization will be done at the group level.',
type: ParameterType.Boolean,
defaultValue: true
})
categorizeByGroup!: boolean;

// For use in static methods
static defaultCategory = 'Other';
static WEIGHTS: string[] = [];

/**
* Create a new CategoryPlugin instance.
*/
initialize() {
this.listenTo(this.owner, {
[Converter.EVENT_BEGIN]: this.onBegin,
[Converter.EVENT_RESOLVE]: this.onResolve,
[Converter.EVENT_RESOLVE_END]: this.onEndResolve
});
}, undefined, -200);
}

/**
* Triggered when the converter begins converting a project.
*
* @param context The context object describing the current state the converter is in.
*/
private onBegin(context: Context) {
// Set up static properties
if (this.defaultCategory) {
CategoryPlugin.defaultCategory = this.defaultCategory;
}
if (this.categoryOrder) {
CategoryPlugin.WEIGHTS = this.categoryOrder;
}
}

/**
Expand All @@ -36,13 +75,7 @@ export class CategoryPlugin extends ConverterComponent {
*/
private onResolve(context: Context, reflection: Reflection) {
if (reflection instanceof ContainerReflection) {
if (reflection.children && reflection.children.length > 0) {
reflection.children.sort(GroupPlugin.sortCallback);
reflection.categories = CategoryPlugin.getReflectionCategories(reflection.children);
}
if (reflection.categories && reflection.categories.length > 1) {
reflection.categories.sort(CategoryPlugin.sortCatCallback);
}
this.categorize(reflection);
}
}

Expand All @@ -52,32 +85,44 @@ export class CategoryPlugin extends ConverterComponent {
* @param context The context object describing the current state the converter is in.
*/
private onEndResolve(context: Context) {
function walkDirectory(directory: SourceDirectory) {
directory.categories = CategoryPlugin.getReflectionCategories(directory.getAllReflections());
const project = context.project;
this.categorize(project);
}

for (let key in directory.directories) {
if (!directory.directories.hasOwnProperty(key)) {
continue;
}
walkDirectory(directory.directories[key]);
}
private categorize(obj: ContainerReflection) {
if (this.categorizeByGroup) {
this.groupCategorize(obj);
} else {
this.lumpCategorize(obj);
}
}

const project = context.project;
if (project.children && project.children.length > 0) {
project.children.sort(GroupPlugin.sortCallback);
project.categories = CategoryPlugin.getReflectionCategories(project.children);
}
if (project.categories && project.categories.length > 1) {
project.categories.sort(CategoryPlugin.sortCatCallback);
private groupCategorize(obj: ContainerReflection) {
if (!obj.groups || obj.groups.length === 0) {
return;
}

walkDirectory(project.directory);
project.files.forEach((file) => {
file.categories = CategoryPlugin.getReflectionCategories(file.reflections);
obj.groups.forEach((group) => {
group.categories = CategoryPlugin.getReflectionCategories(group.children);
if (group.categories && group.categories.length > 1) {
group.categories.sort(CategoryPlugin.sortCatCallback);
} else if (group.categories.length === 1 && group.categories[0].title === CategoryPlugin.defaultCategory) {
// no categories if everything is uncategorized
group.categories = undefined;
}
});
}

private lumpCategorize(obj: ContainerReflection) {
if (obj instanceof ContainerReflection) {
if (obj.children && obj.children.length > 0) {
obj.categories = CategoryPlugin.getReflectionCategories(obj.children);
}
if (obj.categories && obj.categories.length > 1) {
obj.categories.sort(CategoryPlugin.sortCatCallback);
}
}
}

/**
* Create a categorized representation of the given list of reflections.
*
Expand All @@ -86,23 +131,26 @@ export class CategoryPlugin extends ConverterComponent {
*/
static getReflectionCategories(reflections: Reflection[]): ReflectionCategory[] {
const categories: ReflectionCategory[] = [];
let defaultCat: ReflectionCategory | undefined;
reflections.forEach((child) => {
const childCat = CategoryPlugin.getCategory(child);
if (childCat === '') {
return;
}
for (let i = 0; i < categories.length; i++) {
const category = categories[i];

if (category.title !== childCat) {
continue;
if (!defaultCat) {
defaultCat = categories.find(category => category.title === CategoryPlugin.defaultCategory);
if (!defaultCat) {
defaultCat = new ReflectionCategory(CategoryPlugin.defaultCategory);
categories.push(defaultCat);
}
}

defaultCat.children.push(child);
return;
}
let category = categories.find(cat => cat.title === childCat);
if (category) {
category.children.push(child);
return;
}

const category = new ReflectionCategory(childCat);
category = new ReflectionCategory(childCat);
category.children.push(child);
categories.push(category);
});
Expand All @@ -116,29 +164,31 @@ export class CategoryPlugin extends ConverterComponent {
* @returns The category the reflection belongs to
*/
static getCategory(reflection: Reflection): string {
if (reflection.comment) {
const tags = reflection.comment.tags;
function extractCategoryTag(comment: Comment) {
const tags = comment.tags;
if (tags) {
for (let i = 0; i < tags.length; i++) {
if (tags[i].tagName === 'category') {
let tag = tags[i].text;
return (tag.charAt(0).toUpperCase() + tag.slice(1).toLowerCase()).trim();
return tag.trim();
}
}
}
return '';
}
return '';
}

/**
* Callback used to sort reflections by name.
*
* @param a The left reflection to sort.
* @param b The right reflection to sort.
* @returns The sorting weight.
*/
static sortCallback(a: Reflection, b: Reflection): number {
return a.name > b.name ? 1 : -1;
let category = '';
if (reflection.comment) {
category = extractCategoryTag(reflection.comment);
} else if (reflection instanceof DeclarationReflection && reflection.signatures) {
// If a reflection has signatures, use the first category tag amongst them
reflection.signatures.forEach(sig => {
if (sig.comment && category === '') {
category = extractCategoryTag(sig.comment);
}
});
}
return category;
}

/**
Expand All @@ -149,16 +199,16 @@ export class CategoryPlugin extends ConverterComponent {
* @returns The sorting weight.
*/
static sortCatCallback(a: ReflectionCategory, b: ReflectionCategory): number {
const aWeight = CategoryPlugin.WEIGHTS.indexOf(a.title);
const bWeight = CategoryPlugin.WEIGHTS.indexOf(b.title);
if (aWeight < 0 && bWeight < 0) {
return a.title > b.title ? 1 : -1;
}
if (aWeight < 0) {
return 1;
let aWeight = CategoryPlugin.WEIGHTS.indexOf(a.title);
let bWeight = CategoryPlugin.WEIGHTS.indexOf(b.title);
if (aWeight === -1 || bWeight === -1) {
let asteriskIndex = CategoryPlugin.WEIGHTS.indexOf('*');
if (asteriskIndex === -1) { asteriskIndex = CategoryPlugin.WEIGHTS.length; }
if (aWeight === -1) { aWeight = asteriskIndex; }
if (bWeight === -1) { bWeight = asteriskIndex; }
}
if (bWeight < 0) {
return -1;
if (aWeight === bWeight) {
return a.title > b.title ? 1 : -1;
}
return aWeight - bWeight;
}
Expand Down
15 changes: 15 additions & 0 deletions src/lib/models/ReflectionGroup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Reflection, ReflectionKind } from './reflections/abstract';
import { ReflectionCategory } from './ReflectionCategory';

/**
* A group of reflections. All reflections in a group are of the same kind.
Expand Down Expand Up @@ -62,6 +63,11 @@ export class ReflectionGroup {
*/
someChildrenAreExported?: boolean;

/**
* Categories contained within this group.
*/
categories?: ReflectionCategory[];

/**
* Create a new ReflectionGroup instance.
*
Expand Down Expand Up @@ -106,6 +112,15 @@ export class ReflectionGroup {
result['children'] = children;
}

if (this.categories) {
const categories: any[] = [];
this.categories.forEach((category) => {
categories.push(category.toObject());
});

result['categories'] = categories;
}

return result;
}
}
27 changes: 0 additions & 27 deletions src/lib/models/reflections/project.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { SourceFile, SourceDirectory } from '../sources/index';
import { Reflection, ReflectionKind } from './abstract';
import { ContainerReflection } from './container';
import { ReflectionCategory } from '../ReflectionCategory';

/**
* A reflection that represents the root of the project.
Expand All @@ -27,11 +26,6 @@ export class ProjectReflection extends ContainerReflection {
*/
files: SourceFile[] = [];

/**
* All reflections categorized.
*/
categories?: ReflectionCategory[];

/**
* The name of the project.
*
Expand Down Expand Up @@ -123,25 +117,4 @@ export class ProjectReflection extends ContainerReflection {

return undefined;
}

/**
* Return a raw object representation of this reflection.
* @deprecated Use serializers instead
*/
toObject(): any {
Gerrit0 marked this conversation as resolved.
Show resolved Hide resolved
const result = super.toObject();

if (this.categories) {
const categories: any[] = [];
this.categories.forEach((category) => {
categories.push(category.toObject());
});

if (categories.length > 0) {
result['categories'] = categories;
}
}

return result;
}
}
3 changes: 0 additions & 3 deletions src/lib/models/sources/directory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Reflection } from '../reflections/abstract';
import { ReflectionCategory } from '../ReflectionCategory';
import { ReflectionGroup } from '../ReflectionGroup';
import { SourceFile } from './file';

Expand All @@ -23,8 +22,6 @@ export class SourceDirectory {

groups?: ReflectionGroup[];

categories?: ReflectionCategory[];

/**
* A list of all files in this directory.
*/
Expand Down
6 changes: 0 additions & 6 deletions src/lib/models/sources/file.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as Path from 'path';

import { Reflection } from '../reflections/abstract';
import { ReflectionCategory } from '../ReflectionCategory';
import { ReflectionGroup } from '../ReflectionGroup';
import { SourceDirectory } from './directory';

Expand Down Expand Up @@ -81,11 +80,6 @@ export class SourceFile {
*/
groups?: ReflectionGroup[];

/**
* A categorized list of the reflections declared in this file.
*/
categories?: ReflectionCategory[];

/**
* Create a new SourceFile instance.
*
Expand Down
Loading