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
No related branches found
No related tags found
No related merge requests found
Showing
with 506 additions and 42 deletions
......@@ -8,6 +8,7 @@
"root": "src",
"outDir": "dist",
"assets": [
"data",
"assets",
"favicon.ico"
],
......@@ -19,9 +20,12 @@
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"styles.css"
"styles.css",
"../node_modules/codemirror/lib/codemirror.css",
"../node_modules/codemirror/theme/mbo.css"
],
"scripts": [
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
......
# See http://help.github.com/ignore-files/ for more about ignoring files.
/src/data/
# compiled output
/dist
/tmp
......
# RdView
# RagDoc View
This is a viewer for JastAdd documentation generated by RagDoc Builder.
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.1.
......
TODO 0 → 100644
* add thrown type descriptions
* add type hierarchy in type details
* add structured production representation
* make current member filter more noticeable
* Add declared-at info for types.
......@@ -22,7 +22,8 @@
"@angular/router": "^4.0.0",
"core-js": "^2.4.1",
"rxjs": "^5.1.0",
"zone.js": "^0.8.4"
"zone.js": "^0.8.4",
"codemirror": "5.25.2"
},
"devDependencies": {
"@angular/cli": "1.0.1",
......
nav, article, h1 {
font-family: "Roboto",Helvetica,sans-serif;
}
.selected {
background-color: #BBD8DC !important;
}
.types {
list-style-type: none;
margin: 0;
padding: 0;
background-color: #607D8B;
}
.types li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: 1px 0;
padding: .3em 0 0 .7em;
height: 1.6em;
}
.types li:hover {
background-color: #CFD8DC;
}
.type .text {
position: relative;
top: -3px;
}
.topnav {
z-index: 5;
position: fixed;
top: 0;
left: 0;
right: 0;
box-sizing: border-box;
height: 65px;
background-color: #CFD8DC;
box-shadow: 0 0 10px grey;
}
.topnav img {
visibility: hidden;
display: inline-block;
box-sizing: border-box;
width: 65px;
height: 65px;
padding: 15px;
position: absolute;
}
.topnav h1 {
display: inline-block;
vertical-align: middle;
box-sizing: border-box;
line-height: 65px;
padding: 0;
margin: 0 0 0 1em;
pointer-events: none;
white-space: nowrap;
overflow: hidden;
}
.sidenav {
width: 16em;
overflow-y: auto;
overflow-x: hidden;
z-index: 4;
position: fixed;
top: 65px;
left: 0;
bottom: 0;
box-shadow: 0 0 8px #888888;
background: white;
}
.package {
padding: .5em;
}
.group {
cursor: pointer;
padding: .5em .5em .5em 1em;
font-weight: bold;
}
.group img {
float: right;
}
article {
margin-left: 16em;
margin-top: 65px;
margin-bottom: 4em;
padding-top: 1em;
padding-left: 1em;
}
.search {
padding-left: 1em;
padding-top: 1em;
padding-right: 1em;
padding-bottom: 0.7em;
}
.search 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;
}
@media all and (max-width: 1024px) {
.hidemenu {
visibility: hidden;
}
.topnav h1 {
padding: 0 0 0 64px;
}
.topnav img {
visibility: visible;
}
article {
margin-left: 1em;
}
}
<h1>
{{title}}
</h1>
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
describe('AppComponent', function () {
let de: DebugElement;
let comp: AppComponent;
let fixture: ComponentFixture<AppComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
declarations: [ AppComponent ]
})
.compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
de = fixture.debugElement.query(By.css('h1'));
});
it(`should have as title 'app works!'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app works!');
}));
it('should create component', () => expect(comp).toBeDefined() );
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
it('should have expected <h1> text', () => {
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('app works!');
}));
const h1 = de.nativeElement;
expect(h1.innerText).toMatch(/angular/i,
'<h1> should say something about "Angular"');
});
});
import { Component } from '@angular/core';
import { OnInit } from '@angular/core';
import { Package } from './package';
import { Type } from './type';
import { PackageService } from './package.service';
import { SelectionService } from './selection.service';
import { Member } from './member';
import { Parameter } from './parameter';
import { ActivatedRoute, Params } from '@angular/router';
import { Location } from '@angular/common';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
styleUrls: ['./app.component.css'],
template: `
<nav class="topnav">
<img src="assets/hamburger_24px.svg" (click)="showMenu = !showMenu"><h1>{{title}}</h1>
</nav>
<nav class="sidenav" [class.hidemenu]="!showMenu">
<div class="search"><input [(ngModel)]="filter" placeholder="Search..."></div>
<ng-template ngFor let-package [ngForOf]="packages">
<div *ngIf="filteredPackage(package)">
<div class="package"><b>{{package.name}}</b></div>
<ng-template ngFor let-group [ngForOf]="package.groups">
<div *ngIf="filteredGroup(group)">
<div class="group" (click)="group.hidden = !group.hidden">{{typeKindNames[group.kind]}}<img src="assets/vdots_24px.svg" *ngIf="!group.hidden"><img src="assets/hdots_24px.svg" *ngIf="group.hidden"></div>
<ul class="types" [hidden]="group.hidden">
<li *ngFor="let type of group.members | nameFilter:filter" class="type"
[class.selected]="type.id === selectedType"
[routerLink]="['/type', type.id]"
(click)="showMenu = false">
{{type.name}}
</li>
</ul>
</div>
</ng-template>
</div>
</ng-template>
</nav>
<article>
<router-outlet></router-outlet>
</article>
`,
providers: [
PackageService,
SelectionService,
],
})
export class AppComponent {
title = 'app works!';
export class AppComponent implements OnInit {
title = 'ExtendJ API Documentation';
showMenu = false;
packages : Package[];
filter = '';
selectedType = '';
private typeKindNames = {
'ast-class': 'AST CLASSES',
'interface': 'INTERFACES',
'class': 'CLASSES',
};
constructor(private packageService: PackageService,
private selectionService: SelectionService) {
selectionService.selection$.subscribe(id => this.selectedType = id);
}
ngOnInit(): void {
this.packageService.getPackages().then(packages => this.packages = packages);
}
declaredAt(member: Member): string {
if (member.doc) {
return `${member.doc.ragFile}:${member.doc.line}`;
} else {
return "";
}
}
filteredPackage(pkg: Package): boolean {
var filter = this.filter.toLowerCase();
for (var i = 0; i < pkg.groups.length; i++) {
if (this.filteredGroup(pkg.groups[i])) {
return true;
}
}
return false;
}
filteredGroup(group: any): boolean {
var filter = this.filter.toLowerCase();
var filtered = group.members.filter(member => member.name.toLowerCase().indexOf(filter) >= 0);
return filtered.length > 0;
}
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { AppComponent } from './app.component';
import { TypeDetailsComponent } from './type-details.component';
import { ParametersComponent } from './parameters.component';
import { TypeReferenceComponent } from './type-ref.component';
import { NameFilterPipe } from './name-filter.pipe';
import { StringFilterPipe } from './string-filter.pipe';
import { SourceViewComponent } from './source-view/source-view.component';
import { EditorDirective } from './editor.directive';
import { DeclaredAtComponent } from './declared-at/declared-at.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
imports: [
BrowserModule,
FormsModule,
HttpModule
HttpModule,
RouterModule.forRoot([
{
path: 'type/:id',
component: TypeDetailsComponent
},
{
path: 'source/:filename/:line',
component: SourceViewComponent
}
]),
],
declarations: [
AppComponent,
TypeDetailsComponent,
ParametersComponent,
TypeReferenceComponent,
NameFilterPipe,
StringFilterPipe,
SourceViewComponent,
EditorDirective,
DeclaredAtComponent,
],
providers: [],
bootstrap: [AppComponent]
bootstrap: [ AppComponent ]
})
export class AppModule { }
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DeclaredAtComponent } from './declared-at.component';
describe('DeclaredAtComponent', () => {
let component: DeclaredAtComponent;
let fixture: ComponentFixture<DeclaredAtComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ DeclaredAtComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DeclaredAtComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Input } from '@angular/core';
import {Doc} from '../doc';
@Component({
selector: 'declared-at',
template: `
Declared at <a [routerLink]="['/source', filename, line]">{{filepath}}:{{line}}.</a>
`,
})
export class DeclaredAtComponent implements OnInit {
@Input() doc: Doc;
filename: string;
filepath: string;
line: string;
constructor() { }
ngOnInit() {
this.filepath = this.doc.ragFile;
this.filename = this.filepath.replace(/\/|\\/g, '_');
this.line = String(this.doc.line);
}
}
export class Doc {
ast: string;
ragFile: string;
line: number;
description: string;
apilevel: string;
params: string[]
paramDesc(name: string): string {
if (this.params) {
for (var i = 0; i < this.params.length; i++) {
var param = this.params[i];
var index = param.indexOf(' ');
if (index >= 0 && param.substring(0, index) === name) {
return ' : ' + param.substring(index + 1);
}
}
}
return '';
}
static fromJson(json: any): Doc {
var obj = Object.create(Doc.prototype);
return Object.assign(obj, json);
}
}
import { EditorDirective } from './editor.directive';
describe('EditorDirective', () => {
it('should create an instance', () => {
const directive = new EditorDirective();
expect(directive).toBeTruthy();
});
});
import { Directive, ElementRef, Input, OnChanges } from '@angular/core';
import * as CodeMirror from 'codemirror';
import 'codemirror/mode/clike/clike';
import 'codemirror/addon/selection/active-line';
@Directive({
selector: '[appEditor]'
})
export class EditorDirective implements OnChanges {
editor: any;
@Input() sourceText: string;
@Input() sourceLine: number;
constructor(public element: ElementRef) {
this.editor = new CodeMirror.fromTextArea(element.nativeElement, {
mode: 'text/x-java',
theme: 'mbo',
lineNumbers: true,
styleActiveLine: true,
lineWrapping: false,
});
this.editor.setSize('100%', '100%');
}
ngOnChanges() {
this.editor.setValue(this.sourceText || 'no source');
if (this.sourceLine) {
var line = this.sourceLine - 1;
this.editor.scrollIntoView({line: line + 30, ch: 0});
this.editor.setCursor({line: line, ch: 0});
}
}
}
import {TypeRef} from './type-ref';
export class InheritedMembers {
superclass: TypeRef;
members: string[];
static fromJson(json: any): InheritedMembers {
return {
superclass: TypeRef.fromJson(json.superclass),
members: json.members as string[],
};
}
}
import { TestBed, inject } from '@angular/core/testing';
import { MemberFilterService } from './member-filter.service';
describe('MemberFilterService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [MemberFilterService]
});
});
it('should ...', inject([MemberFilterService], (service: MemberFilterService) => {
expect(service).toBeTruthy();
}));
});
import { Injectable } from '@angular/core';
import {Subject} from 'rxjs/Subject';
@Injectable()
export class MemberFilterService {
private filter = new Subject<string>();
filter$ = this.filter.asObservable();
constructor() { }
setFilter(filter: string) {
this.filter.next(filter);
}
}
import {Parameter} from './parameter';
import {Doc} from './doc';
import {TypeRef} from './type-ref';
export class Member {
name: string;
type: TypeRef;
doc: Doc;
parameters: Parameter[];
throws: TypeRef[];
static fromJson(json: any): Member {
var params: Parameter[] = [];
var doc: Doc = undefined;
if (json.doc) {
doc = Doc.fromJson(json.doc);
}
if (json.params) {
params = json.params as Parameter[];
}
var throws: TypeRef[] = undefined;
if (json.throws) {
throws = (json.throws as TypeRef[]).map(TypeRef.fromJson);
}
if (json.type) {
return Object.assign({}, json, {
type: TypeRef.fromJson(json.type),
parameters: params.map(param => Parameter.fromJson(param)),
doc: doc,
throws: throws,
});
} else {
return Object.assign({}, json, {
parameters: params.map(param => Parameter.fromJson(param)),
doc: doc,
throws: throws,
});
}
}
}
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'nameFilter'
})
export class NameFilterPipe implements PipeTransform {
transform(items: any[], filter: string): any {
if (!items || !filter) {
return items;
}
return items.filter(item => item.name.toLowerCase().indexOf(filter.toLowerCase()) >= 0);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment