Skip to content
Snippets Groups Projects
Commit a860b0c0 authored by Jesper Öqvist's avatar Jesper Öqvist Committed by Jesper
Browse files

Basic JastAdd Docs

This is an Angular 4.0 application for viewing JastAdd documentation.

CodeMirror is used to display JastAdd source code.
parent 3739bc3e
Branches
No related tags found
No related merge requests found
Showing
with 628 additions and 0 deletions
import {TypeRef} from './type-ref';
export class PackageEntry {
kind: string;
name: string;
id: string;
static fromJson(json: any): PackageEntry {
return json as PackageEntry;
}
}
import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { Package } from './package';
@Injectable()
export class PackageService {
private packageUrl = 'data/packages.json';
constructor(private http: Http) { }
getPackages(): Promise<Package[]> {
return this.http.get(this.packageUrl)
.toPromise()
.then(response => (response.json().data as Package[]).map(pkg => Package.fromJson(pkg)))
.catch(res => Promise.reject(`Failed to load packages: ${res}`));
}
}
import { PackageEntry } from './package-entry';
export class Package {
name: string;
groups: any[];
static fromJson(json: any): Package {
return json as Package;
}
}
import {TypeRef} from './type-ref';
export class Parameter {
type: TypeRef;
name: string;
static fromJson(json: any): Parameter {
return {
type: TypeRef.fromJson(json.t),
name: json.n,
};
}
}
import { Component, Input, ComponentFactoryResolver } from '@angular/core';
import {Parameter} from './parameter';
@Component({
selector: 'parameters',
styles: [`
.parameters {
display: inline;
height: 1.2em;
overflow: hidden;
text-overflow: ellipsis;
}
.parameter {
display: inline;
}
.sep {
display: inline;
margin-right: .3em;
}
`],
template: `
<div class="parameters">
<div *ngFor="let param of params; let isLast=last" class="parameter">
<type-ref [type]="param.type"></type-ref> {{param.name}}<div *ngIf="!isLast" class="sep">,</div>
</div>
</div>
`
})
export class ParametersComponent {
@Input() params : Parameter[];
constructor(private _componentFactoryResolver: ComponentFactoryResolver) { }
}
import {Injectable} from '@angular/core';
import {Subject} from 'rxjs/Subject';
@Injectable()
export class SelectionService {
private selection = new Subject<string>();
selection$ = this.selection.asObservable();
select(id: string) {
this.selection.next(id);
}
}
<p>
source-view works!
</p>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SourceViewComponent } from './source-view.component';
describe('SourceViewComponent', () => {
let component: SourceViewComponent;
let fixture: ComponentFixture<SourceViewComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SourceViewComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SourceViewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import 'rxjs/add/operator/switchMap';
import {SourceService} from '../source.service';
@Component({
selector: 'app-source-viewer',
providers: [ SourceService ],
template: `<textarea appEditor [sourceText]="source" [sourceLine]="line">{{source}}</textarea>`,
})
export class SourceViewComponent implements OnInit {
source: string;
line = 0;
constructor(private sourceService: SourceService,
private route: ActivatedRoute) { }
ngOnInit() {
this.route.params.switchMap((params: Params) => {
this.line = +params['line'];
return this.sourceService.getSource(params['filename']);
})
.subscribe(source => {
this.source = source;
});
}
}
import { TestBed, inject } from '@angular/core/testing';
import { SourceService } from './source.service';
describe('SourceService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [SourceService]
});
});
it('should ...', inject([SourceService], (service: SourceService) => {
expect(service).toBeTruthy();
}));
});
import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';
@Injectable()
export class SourceService {
constructor(private http: Http) { }
getSource(filename: String): Promise<string> {
return this.http.get(`data/${filename}`)
.toPromise()
.then(response => response.text())
.catch(res => Promise.reject(`Failed to load source: ${name}: ${res}.`));
}
}
import { StringFilterPipe } from './string-filter.pipe';
describe('StringFilterPipe', () => {
it('create an instance', () => {
const pipe = new StringFilterPipe();
expect(pipe).toBeTruthy();
});
});
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'stringFilter'
})
export class StringFilterPipe implements PipeTransform {
transform(items: any[], filter: string): any {
if (!items || !filter) {
return items;
}
return items.filter(item => item.toLowerCase().indexOf(filter.toLowerCase()) >= 0);
}
}
.doc-signature {
display: inline-block;
height: 1.3em;
width: calc(100% - 50px); /* Avoids line wrap in summary. */
overflow: hidden;
vertical-align: middle;
}
.attribute-kind {
color: #888;
}
.member-type {
width: 12em;
overflow: hidden;
display: inline-block;
vertical-align: top;
}
.member-name {
font-weight: bold;
}
.doc-preview {
display: inline;
float: right;
width: 40%;
line-height: 1.2em;
height: 1.2em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: top;
}
summary {
cursor: pointer;
padding: .5em 0 .5em 0;
}
.member-details {
padding-bottom: .5em;
}
.members {
list-style-type: none;
margin: 0;
padding: 0;
}
.members li {
background-color: #EEE;
margin: .2em 0;
padding: 0 .7em 0 .7em;
border-radius: 4px;
}
.return {
display: inline-block;
}
.sep {
display: inline;
margin-right: .3em;
}
.filter input {
border: 4px solid grey;
border-radius: 4px;
background: white url('../assets/search_grey_24px.svg') 10px 8px no-repeat;
padding: 10px 20px 10px 40px;
font-size: 16px;
width: 144px;
}
.filter button {
border: 4px solid grey;
border-radius: 4px;
padding: 10px;
font-size: 16px;
background: lightgrey;
}
@media all and (max-width: 1024px) {
.doc-preview {
visibility: hidden;
}
}
@media all and (max-width: 500px) {
.member-type {
visibility: hidden;
width: 0;
}
}
<div *ngIf="type">
<h2>{{type.kind}} {{type.name}}</h2>
<p *ngIf="type.superclass">extends <type-ref [type]="type.superclass"></type-ref>
<ng-container *ngIf="type.superinterfaces">implements <ng-container *ngFor="let iface of type.superinterfaces; let isLast = last"><type-ref [type]="iface"></type-ref><div *ngIf="!isLast" class="sep">, </div></ng-container></ng-container>
</p>
<div *ngIf="type.doc">
<p [innerHTML]="type.doc.description">
<p *ngIf="type.doc.astdecl">JastAdd production: {{type.doc.astdecl}}
<p *ngIf="type.doc && type.doc.ragFile"><declared-at [doc]="type.doc"></declared-at>
</div>
<div class="filter">
<input [(ngModel)]="filter" placeholder="Filter members...">
<button (click)="clearFilter()" [hidden]="filter === ''">Clear filter</button>
</div>
<div *ngIf="filteredMembers('constr')">
<h3>Constructors</h3>
<ul class="members">
<li *ngFor="let member of type.groups['constr'] | nameFilter:filter">
<details>
<summary>
<div class="doc-signature"><span class="member-name">{{member.name}}</span> ( <parameters [params]="member.parameters"></parameters> )
<div class="doc-preview" *ngIf="member.doc" [innerHTML]="member.doc.description"></div></div>
</summary>
<div class="member-details">
<p *ngIf="member.doc" [innerHTML]="member.doc.description"></p>
<p *ngIf="member.doc && member.doc.ragFile"><declared-at [doc]="member.doc"></declared-at>
<p *ngFor="let param of member.parameters; let index = index"><b>Parameter {{index+1}}</b> <type-ref [type]="param.type"></type-ref> <b>{{param.name}}</b><span *ngIf="member.doc" [innerHTML]="paramDesc(member.doc, param.name)"></span></p>
<p *ngIf="member.throws">Throws <ng-container *ngFor="let excp of member.throws; let isLast=last"><type-ref [type]="excp"></type-ref><div *ngIf="!isLast" class="sep">, </div></ng-container></p>
</div>
</details>
</li>
</ul>
</div>
<div *ngIf="filteredMembers('attr')">
<h3>Attributes</h3>
<ul class="members">
<li *ngFor="let member of type.groups['attr'] | nameFilter:filter">
<details>
<summary>
<div class="doc-signature"><div class="member-type"><span *ngIf="member.doc" class="attribute-kind">{{member.doc.attribute}}</span> <type-ref [type]="member.type"></type-ref></div> <span class="member-name">{{member.name}}</span> ( <parameters [params]="member.parameters"></parameters> )
<div class="doc-preview" *ngIf="member.doc" [innerHTML]="member.doc.description"></div></div>
</summary>
<div class="member-details">
<p *ngIf="member.doc" [innerHTML]="member.doc.description"></p>
<p *ngIf="member.doc && member.doc.ragFile"><declared-at [doc]="member.doc"></declared-at>
<p *ngFor="let param of member.parameters; let index = index"><b>Parameter {{index+1}}</b> <type-ref [type]="param.type"></type-ref> <b>{{param.name}}</b><span *ngIf="member.doc" [innerHTML]="paramDesc(member.doc, param.name)"></span></p>
<p><b>Returns</b> <type-ref [type]="member.type"></type-ref><ng-container *ngIf="member.doc && member.doc.return"> : <span class="return" *ngIf="member.doc" [innerHTML]="member.doc.return"></span></ng-container>
</div>
</details>
</li>
</ul>
</div>
<div *ngIf="type.inherited_attributes">
<ng-container *ngFor="let inherited of type.inherited_attributes">
<ng-container *ngIf="inheritedMembers(inherited)">
<h3>Attributes inherited from <type-ref [type]="inherited.superclass"></type-ref></h3>
<ng-container *ngFor="let member of inherited.members | stringFilter:filter; let isLast = last"><type-ref [type]="inherited.superclass" [name]="member" [filter]="member"></type-ref><ng-container *ngIf="!isLast">, </ng-container></ng-container>
</ng-container>
</ng-container>
</div>
<div *ngIf="filteredMembers('field')">
<h3>Fields</h3>
<ul class="members">
<li *ngFor="let member of type.groups['field'] | nameFilter:filter">
<details>
<summary>
<div class="doc-signature"><div class="member-type"><type-ref [type]="member.type"></type-ref></div> <span class="member-name">{{member.name}}</span>
<div class="doc-preview" *ngIf="member.doc" [innerHTML]="member.doc.description"></div></div>
</summary>
<p *ngIf="member.doc" [innerHTML]="member.doc.description"></p>
<p *ngIf="member.doc && member.doc.ragFile"><declared-at [doc]="member.doc"></declared-at>
</details>
</li>
</ul>
</div>
<div *ngIf="type.inherited_fields">
<ng-container *ngFor="let inherited of type.inherited_fields">
<ng-container *ngIf="inheritedMembers(inherited)">
<h3>Fields inherited from <type-ref [type]="inherited.superclass"></type-ref></h3>
<ng-container *ngFor="let member of inherited.members | stringFilter:filter; let isLast = last"><type-ref [type]="inherited.superclass" [name]="member" [filter]="member"></type-ref><ng-container *ngIf="!isLast">, </ng-container></ng-container>
</ng-container>
</ng-container>
</div>
<div *ngIf="filteredMembers('method')">
<h3>Methods</h3>
<ul class="members">
<li *ngFor="let member of type.groups['method'] | nameFilter:filter">
<details>
<summary>
<div class="doc-signature"><div class="member-type"><type-ref [type]="member.type"></type-ref></div> <span class="member-name">{{member.name}}</span> ( <parameters [params]="member.parameters"></parameters> )
<div class="doc-preview" *ngIf="member.doc" [innerHTML]="member.doc.description"></div></div>
</summary>
<div class="member-details">
<p *ngIf="member.doc" [innerHTML]="member.doc.description"></p>
<p *ngIf="member.doc && member.doc.ragFile"><declared-at [doc]="member.doc"></declared-at>
<p *ngFor="let param of member.parameters; let index = index"><b>Parameter {{index+1}}</b> <type-ref [type]="param.type"></type-ref> <b>{{param.name}}</b><span *ngIf="member.doc" [innerHTML]="paramDesc(member.doc, param.name)"></span></p>
<p><b>Returns</b> <type-ref [type]="member.type"></type-ref><ng-container *ngIf="member.doc && member.doc.return"> : <span class="return" *ngIf="member.doc" [innerHTML]="member.doc.return"></span></ng-container>
<p *ngIf="member.throws">Throws <ng-container *ngFor="let excp of member.throws; let isLast=last"><type-ref [type]="excp"></type-ref><div *ngIf="!isLast" class="sep">, </div></ng-container></p>
</div>
</details>
</li>
</ul>
</div>
<div *ngIf="type.inherited_methods">
<ng-container *ngFor="let inherited of type.inherited_methods">
<ng-container *ngIf="inheritedMembers(inherited)">
<h3>Methods inherited from <type-ref [type]="inherited.superclass"></type-ref></h3>
<ng-container *ngFor="let member of inherited.members | stringFilter:filter; let isLast = last"><type-ref [type]="inherited.superclass" [name]="member" [filter]="member"></type-ref><ng-container *ngIf="!isLast">, </ng-container></ng-container>
</ng-container>
</ng-container>
</div>
</div>
import { Component } from '@angular/core';
import { OnInit } from '@angular/core';
import { Package } from './package';
import { Type } from './type';
import { TypeService } from './type.service';
import { MemberFilterService } from './member-filter.service';
import { SelectionService } from './selection.service';
import { Member } from './member';
import { InheritedMembers } from './inherited-members';
import {Doc} from './doc';
import { Parameter } from './parameter';
import { ActivatedRoute, Params } from '@angular/router';
import { Location } from '@angular/common';
import 'rxjs/add/operator/switchMap';
@Component({
selector: 'type-details',
styleUrls: ['./type-details.component.css'],
templateUrl: './type-details.component.html',
providers: [
TypeService,
MemberFilterService,
],
})
export class TypeDetailsComponent implements OnInit {
type: Type = Object.create(Type.prototype);
filter: string = '';
constructor(private typeService: TypeService,
private memberFilterService: MemberFilterService,
private route: ActivatedRoute,
private location: Location,
private selectionService: SelectionService) {
memberFilterService.filter$.subscribe(filter => this.filter = filter);
}
ngOnInit() {
this.route.params.switchMap((params: Params) => {
return this.typeService.getType(params['id'])
})
.subscribe(type => {
this.selectionService.select(type.id);
this.type = type;
});
}
declaredAt(doc: Doc): string {
return `${doc.ragFile}:${doc.line}`;
}
paramDesc(doc: Doc, name: string): string {
if (doc) {
return doc.paramDesc(name);
}
return '';
}
filteredMembers(kind: string): boolean {
if (this.type.groups && this.type.groups[kind]) {
var filter = this.filter.toLowerCase();
var filtered = this.type.groups[kind].filter(item =>
item.name.toLowerCase().indexOf(filter) >= 0)
return filtered.length > 0;
}
return false;
}
inheritedMembers(inherited: InheritedMembers): boolean {
var filter = this.filter.toLowerCase();
var filtered = inherited.members.filter(item => item.toLowerCase().indexOf(filter) >= 0)
return filtered.length > 0;
}
clearFilter() {
this.filter = '';
}
}
import { Component, Input, ComponentFactoryResolver } from '@angular/core';
import {Type} from './type';
import { MemberFilterService } from './member-filter.service';
@Component({
selector: 'type-ref',
styles: [`
.sep {
display: inline;
margin-right: .3em;
}
.usertype {
color: #8c1339;
text-decoration: none;
}
.non-usertype {
color: #444;
}
`],
template: `<a *ngIf="type.id; else elseBlock" class="usertype" [routerLink]="['/type', type.id]" (click)="onClick()">{{getName()}}</a><!--
--><ng-template #elseBlock><span class="non-usertype">{{getName()}}</span></ng-template><!--
--><ng-container *ngIf="type.args && !name"><!--
-->&lt;<ng-container *ngFor="let arg of type.args; let isLast=last"><type-ref [type]="arg"></type-ref><div *ngIf="!isLast" class="sep">,</div></ng-container>&gt;<!--
--></ng-container>`
})
export class TypeReferenceComponent {
@Input() type : Type;
@Input() name : string;
@Input() filter : string;
constructor(private _componentFactoryResolver: ComponentFactoryResolver,
private memberFilterService: MemberFilterService) { }
getName(): string {
if (this.name) {
return this.name;
} else {
return this.type.name;
}
}
onClick() {
if (this.filter) {
this.memberFilterService.setFilter(this.filter);
}
}
}
export class TypeRef {
name: string;
id: string;
args: TypeRef[];
static fromJson(json: any): TypeRef {
var args: TypeRef[] = undefined;
if (json.a) {
args = (json.a as TypeRef[]).map(TypeRef.fromJson);
}
if (json.u) {
// User type.
if (json.i) {
return {
name: json.u,
id: TypeRef.typeId(json.u, json.i),
args: args,
};
} else {
return {
name: json.u,
id: TypeRef.simpleName(json.u),
args: args,
};
}
} else {
// Library or built-in type.
return {
name: json.n,
id: undefined,
args: args,
};
}
}
// Remove array and type args.
static simpleName(name: string): string {
return TypeRef.strip('<', TypeRef.strip('[', name));
}
static strip(tok: string, name: string): string {
const index = name.indexOf(tok);
if (index < 0) {
return name;
} else {
var simple = name.substring(0, index);
return simple;
}
}
static typeId(name: string, idPattern: string): string {
return idPattern.replace('%', TypeRef.simpleName(name));
}
}
import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { Type } from './type';
@Injectable()
export class TypeService {
constructor(private http: Http) { }
getType(id: string): Promise<Type> {
const typeUrl = `data/${id}.json`;
return this.http.get(typeUrl)
.toPromise()
.then(response => Type.fromJson(response.json().data))
.catch(res => Promise.reject(`Failed to load type: ${id}: ${res}.`));
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment