Skip to content

Commit

Permalink
✨ Add annotations to GDocs paste buffers (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-evans committed Oct 11, 2018
1 parent 0cf70f9 commit 32972dc
Show file tree
Hide file tree
Showing 29 changed files with 274 additions and 151 deletions.
7 changes: 3 additions & 4 deletions packages/@atjson/document/src/annotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import Document from './index';
import JSON from './json';

export type ConcreteAnnotation<T extends Annotation> = T;
export type AnyAnnotation = ConcreteAnnotation<any>;
export interface AnnotationConstructor {
vendorPrefix: string;
type: string;
subdocuments: { [key: string]: typeof Document };
new(attributes: { id: string, start: number, end: number, attributes: Attributes }): AnyAnnotation;
hydrate(attrs: { id: string, start: number, end: number, attributes: JSON }): AnyAnnotation;
new(attributes: { id: string, start: number, end: number, attributes: Attributes }): Annotation;
hydrate(attrs: { id: string, start: number, end: number, attributes: JSON }): Annotation;
}

export default abstract class Annotation {
Expand Down Expand Up @@ -170,7 +169,7 @@ export default abstract class Annotation {
type: `-${vendorPrefix}-${this.type}`,
start: this.start,
end: this.end,
attributes: toJSON(vendorPrefix, this.attributes) as any
attributes: toJSON(vendorPrefix, this.attributes)
};
}
}
4 changes: 2 additions & 2 deletions packages/@atjson/document/src/annotations/unknown.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Annotation from '../annotation';
import { Attributes } from '../attributes';
import JSON from '../json';

export default class UnknownAnnotation extends Annotation {
static vendorPrefix = 'atjson';
static type = 'unknown';
attributes!: {
type: string;
attributes: Attributes;
attributes: JSON;
};

get rank() {
Expand Down
8 changes: 8 additions & 0 deletions packages/@atjson/document/src/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ class Collection {
}
}

get length() {
return this.annotations.length;
}

map<T>(mapper: (annotation: Annotation) => T) {
return this.annotations.map(mapper);
}

where(filter: { [key: string]: any; } | ((annotation: Annotation) => boolean)) {
if (filter instanceof Function) {
return new AnnotationCollection(this.document, this.annotations.filter(filter));
Expand Down
7 changes: 3 additions & 4 deletions packages/@atjson/document/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Annotation, { AnnotationConstructor, AnyAnnotation } from './annotation';
import Annotation, { AnnotationConstructor } from './annotation';
import { Block, Inline, Object, Parse, Unknown } from './annotations';
import { Attribute, Attributes } from './attributes';
import Change, { AdjacentBoundaryBehaviour, Deletion, Insertion } from './change';
Expand All @@ -11,15 +11,14 @@ export interface AnnotationJSON {
type: string;
start: number;
end: number;
attributes: Attributes;
attributes: JSON;
}

export {
AdjacentBoundaryBehaviour,
Annotation,
AnnotationCollection,
AnnotationConstructor,
AnyAnnotation,
Attribute,
Attributes,
Block as BlockAnnotation,
Expand All @@ -36,7 +35,7 @@ export {

export default class Document {
static contentType: string;
static schema: AnyAnnotation[] = [];
static schema: AnnotationConstructor[] = [];

content: string;
readonly contentType: string;
Expand Down
57 changes: 44 additions & 13 deletions packages/@atjson/document/test/query-test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
import { AnnotationJSON, Attributes } from '../src';
import TestSource, { Anchor } from './test-source';
import TestSource, { Anchor, Code, Preformatted, Locale } from './test-source';

describe('Document.where', () => {
it('length on collections', () => {
let doc = new TestSource({
content: 'Hello',
annotations: [{
id: '1',
type: '-test-bold',
start: 0,
end: 5,
attributes: {}
}]
});

expect(doc.where({ type: '-test-bold' }).length).toEqual(1);
expect(doc.where({ type: '-test-italic' }).length).toEqual(0);
});

it('map over collections', () => {
let doc = new TestSource({
content: 'Hello',
annotations: [{
id: '1',
type: '-test-bold',
start: 0,
end: 5,
attributes: {}
}]
});

expect(doc.where({ type: '-test-bold' }).map(a => a.type)).toEqual(['bold']);
});

it('set', () => {
let doc = new TestSource({
content: 'Hello',
Expand Down Expand Up @@ -179,24 +210,24 @@ describe('Document.where', () => {
}]
});

doc.where({ type: '-test-code' }).update(annotation => {
let annotations = doc.replaceAnnotation(annotation, {
id: annotation.id + '-1',
doc.where({ type: '-test-code' }).update((code: Code) => {
let annotations = doc.replaceAnnotation(code, {
id: code.id + '-1',
type: '-test-pre',
start: annotation.start,
end: annotation.end,
attributes: annotation.toJSON().attributes as Attributes
start: code.start,
end: code.end,
attributes: code.toJSON().attributes
}, {
id: annotation.id + '-2',
id: code.id + '-2',
type: '-test-code',
start: annotation.start,
end: annotation.end,
start: code.start,
end: code.end,
attributes: {}
});

return {
add: annotations,
remove: [annotation]
remove: [code]
};
}).unset('attributes.-test-class');

Expand Down Expand Up @@ -290,7 +321,7 @@ describe('Document.where', () => {
}]
}]);

preAndCode.update(({ code, pre }) => {
preAndCode.update(({ code, pre }: { code: Code, pre: Preformatted[] }) => {
doc.removeAnnotation(pre[0]);
code.attributes.textStyle = 'pre';
doc.deleteText(2, 4);
Expand Down Expand Up @@ -412,7 +443,7 @@ describe('Document.where', () => {
}]
}]);

threeWayJoin.update(({ code, preElements, locale }) => {
threeWayJoin.update(({ code, preElements, locale }: { code: Code, preElements: Preformatted[], locale: Locale[] }) => {
doc.insertText(0, 'Hello!\n');

let newCode = code.clone();
Expand Down
4 changes: 2 additions & 2 deletions packages/@atjson/hir/src/hir-node.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Document, { Annotation, AnyAnnotation, Attribute, JSON, ParseAnnotation } from '@atjson/document';
import Document, { Annotation, Attribute, JSON, ParseAnnotation } from '@atjson/document';
import { Root, Text } from './annotations';
import HIR from './hir';

Expand Down Expand Up @@ -66,7 +66,7 @@ function toJSON(attribute: HIRAttribute): JSON {

export default class HIRNode {

annotation: AnyAnnotation;
annotation: Annotation;
id: string;
attributes: HIRAttributes;
start: number;
Expand Down
8 changes: 4 additions & 4 deletions packages/@atjson/renderer-hir/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Document, { AnyAnnotation } from '@atjson/document';
import Document, { Annotation } from '@atjson/document';
import { HIR, HIRNode, TextAnnotation } from '@atjson/hir';

interface Mapping {
Expand Down Expand Up @@ -34,8 +34,8 @@ function flatten(array: any[]): any[] {
return flattenedArray;
}

function compile(renderer: Renderer, node: HIRNode, parent?: AnyAnnotation, index?: number): any {
let annotation: AnyAnnotation = node.annotation.clone();
function compile(renderer: Renderer, node: HIRNode, parent?: Annotation, index?: number): any {
let annotation: Annotation = node.annotation.clone();
let children = node.children();

// Add metadata to annotations for formats that require context
Expand Down Expand Up @@ -85,7 +85,7 @@ function compile(renderer: Renderer, node: HIRNode, parent?: AnyAnnotation, inde

export default class Renderer {

*renderAnnotation(annotation: AnyAnnotation): IterableIterator<any> {
*renderAnnotation(annotation: Annotation): IterableIterator<any> {
let generator = (this as any)[annotation.type];
if (generator) {
return yield* generator.call(this, annotation);
Expand Down
4 changes: 2 additions & 2 deletions packages/@atjson/renderer-hir/test/renderer-hir-test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Document, { AnyAnnotation, InlineAnnotation } from '@atjson/document';
import Document, { Annotation, InlineAnnotation } from '@atjson/document';
import { HIR, HIRNode } from '@atjson/hir';
import HIRRenderer, { escapeHTML } from '../src/index';

Expand Down Expand Up @@ -66,7 +66,7 @@ describe('@atjson/renderer-hir', () => {
];

class ConcreteRenderer extends HIRRenderer {
*renderAnnotation(annotation: AnyAnnotation): IterableIterator<any> {
*renderAnnotation(annotation: Annotation): IterableIterator<any> {
let expected = callStack.shift();
expect(annotation.toJSON()).toEqual(expected.annotation.toJSON());

Expand Down
6 changes: 6 additions & 0 deletions packages/@atjson/source-gdocs-paste/src/annotations/bold.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { InlineAnnotation } from '@atjson/document';

export default class Bold extends InlineAnnotation {
static vendorPrefix = 'gdocs';
static type = 'ts_bd'; // Text style: bold
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { BlockAnnotation } from '@atjson/document';

export default class Heading extends BlockAnnotation {
static vendorPrefix = 'gdocs';
static type = 'ps_hd';
attributes!: {
level: 1 | 2 | 3 | 4 | 5 | 6 | 100 | 101;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ObjectAnnotation } from '@atjson/document';

export default class HorizontalRule extends ObjectAnnotation {
static vendorPrefix = 'gdocs';
static type = 'horizontal_rule';
}
10 changes: 10 additions & 0 deletions packages/@atjson/source-gdocs-paste/src/annotations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export { default as Bold } from './bold';
export { default as Heading } from './heading';
export { default as HorizontalRule } from './horizontal-rule';
export { default as Italic } from './italic';
export { default as Link } from './link';
export { default as ListItem } from './list-item';
export { default as List } from './list';
export { default as Strikethrough } from './strikethrough';
export { default as Underline } from './underline';
export { default as VerticalAdjust } from './vertical-adjust';
6 changes: 6 additions & 0 deletions packages/@atjson/source-gdocs-paste/src/annotations/italic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { InlineAnnotation } from '@atjson/document';

export default class Italic extends InlineAnnotation {
static vendorPrefix = 'gdocs';
static type = 'ts_it'; // Text style: italic
}
10 changes: 10 additions & 0 deletions packages/@atjson/source-gdocs-paste/src/annotations/link.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { InlineAnnotation } from '@atjson/document';

export default class Link extends InlineAnnotation {
static vendorPrefix = 'gdocs';
static type = 'lnks_link';
attributes!: {
ulnk_url: string;
lnk_type: number;
};
}
10 changes: 10 additions & 0 deletions packages/@atjson/source-gdocs-paste/src/annotations/list-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { BlockAnnotation } from '@atjson/document';

export default class ListItem extends BlockAnnotation {
static vendorPrefix = 'gdocs';
static type = 'list_item';
attributes!: {
ls_id: string;
ls_nest: number;
};
}
12 changes: 12 additions & 0 deletions packages/@atjson/source-gdocs-paste/src/annotations/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { BlockAnnotation } from '@atjson/document';

export default class List extends BlockAnnotation {
static vendorPrefix = 'gdocs';
static type = 'list';
attributes!: {
ls_id: string;
ls_b_gs: string;
ls_b_gt: number;
ls_b_a: number;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { InlineAnnotation } from '@atjson/document';

export default class Strikethrough extends InlineAnnotation {
static vendorPrefix = 'gdocs';
static type = 'ts_st'; // Text style: strikethrough
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { InlineAnnotation } from '@atjson/document';

export default class Underline extends InlineAnnotation {
static vendorPrefix = 'gdocs';
static type = 'ts_un'; // Text style: underline
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { InlineAnnotation } from '@atjson/document';

export default class VerticalAdjust extends InlineAnnotation {
static vendorPrefix = 'gdocs';
static type = 'ts_va'; // Text style: underline
attributes!: {
va: 'sub' | 'sup'; // Vertical adjust: subscript / superscript
};
}
14 changes: 7 additions & 7 deletions packages/@atjson/source-gdocs-paste/src/gdocs-parser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Annotation } from '@atjson/document';
import { AnnotationJSON } from '@atjson/document';

import { GDocsEntityMap, GDocsStyleSlice } from './types';

Expand All @@ -8,12 +8,12 @@ import extractListStyles from './list-styles';
import extractParagraphStyles from './paragraph-styles';
import extractTextStyles from './text-styles';

export interface GDocsSource {
export interface GDocsPasteBuffer {
[key: string]: any;
}

export interface Transforms {
[key: string]: (styles: GDocsStyleSlice[], entityMap: GDocsEntityMap) => Annotation[];
[key: string]: (styles: GDocsStyleSlice[], entityMap: GDocsEntityMap) => AnnotationJSON[];
}

export default class GDocsParser {
Expand All @@ -26,17 +26,17 @@ export default class GDocsParser {
link: extractLinkStyles
};

gdocsSource: GDocsSource;
gdocsSource: GDocsPasteBuffer;

constructor(source: GDocsSource) {
constructor(source: GDocsPasteBuffer) {
this.gdocsSource = source;
}

getContent(): string {
return this.gdocsSource.resolved.dsl_spacers;
}

getAnnotations(): Annotation[] {
getAnnotations(): AnnotationJSON[] {
const styleSlices = this.gdocsSource.resolved.dsl_styleslices;
const transforms = GDocsParser.transforms;
const entityMap: GDocsEntityMap = this.gdocsSource.resolved.dsl_entitymap;
Expand All @@ -51,6 +51,6 @@ export default class GDocsParser {
return null;
});

return [].concat.apply([], annotations).filter((a: Annotation | null) => a != null);
return [].concat.apply([], annotations).filter((a: AnnotationJSON | null) => a != null);
}
}
7 changes: 3 additions & 4 deletions packages/@atjson/source-gdocs-paste/src/horizontal-rule.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Annotation } from '@atjson/document';
import { HorizontalRule } from './schema';
import { AnnotationJSON } from '@atjson/document';
import { GDocsStyleSlice } from './types';

export default function extractHorizontalRule(styles: GDocsStyleSlice[]): Annotation[] {
let annotations: HorizontalRule[] = [];
export default function extractHorizontalRule(styles: GDocsStyleSlice[]): AnnotationJSON[] {
let annotations: AnnotationJSON[] = [];

for (let i = 0; i < styles.length; i++) {
let style = styles[i];
Expand Down
Loading

0 comments on commit 32972dc

Please sign in to comment.