This is an automated email from the ASF dual-hosted git repository. gerben pushed a commit to branch import-dom-seek in repository https://gitbox.apache.org/repos/asf/incubator-annotator.git
commit 15aec219ed6e489a8e6d405dcf1b524ef6cb523f Author: Gerben <[email protected]> AuthorDate: Mon Nov 16 23:00:13 2020 +0100 Make ChunkSeeker interface, drop BoundaryPointer/DomSeeker --- packages/dom/src/chunker.ts | 2 +- packages/dom/src/code-point-seeker.ts | 67 +++++++++++++++++++++++++++-------- packages/dom/src/seek.ts | 33 +++++------------ 3 files changed, 62 insertions(+), 40 deletions(-) diff --git a/packages/dom/src/chunker.ts b/packages/dom/src/chunker.ts index e636509..78b8646 100644 --- a/packages/dom/src/chunker.ts +++ b/packages/dom/src/chunker.ts @@ -24,7 +24,7 @@ import { ownerDocument } from "./owner-document"; // A Chunk represents a fragment (typically a string) of some document. // Subclasses can add further attributes to map the chunk to its position in the // data structure it came from (e.g. a DOM node). -export interface Chunk<TData extends any> { +export interface Chunk<TData> { readonly data: TData; equals?(otherChunk: this): boolean; } diff --git a/packages/dom/src/code-point-seeker.ts b/packages/dom/src/code-point-seeker.ts index 9adc89c..b0a95cd 100644 --- a/packages/dom/src/code-point-seeker.ts +++ b/packages/dom/src/code-point-seeker.ts @@ -18,12 +18,13 @@ * under the License. */ -import { Seeker, BoundaryPointer } from "./seek"; +import { ChunkSeeker } from "./seek"; +import { Chunk } from "./chunker"; -class _CodePointSeeker implements Seeker<string[]> { +export class CodePointSeeker<TChunk extends Chunk<string>> implements ChunkSeeker<TChunk, string[]> { position = 0; - constructor(public readonly raw: Seeker<string>) {} + constructor(public readonly raw: ChunkSeeker<TChunk>) {} seekBy(length: number) { this.seekTo(this.position + length); @@ -41,6 +42,54 @@ class _CodePointSeeker implements Seeker<string[]> { return this._readOrSeekTo(true, target, roundUp); } + get currentChunk() { + return this.raw.currentChunk; + } + + get offsetInChunk() { + return this.raw.offsetInChunk; + } + + seekToChunk(target: TChunk, offset: number = 0) { + this._readOrSeekToChunk(false, target, offset); + } + + readToChunk(target: TChunk, offset: number = 0) { + return this._readOrSeekToChunk(true, target, offset); + } + + private _readOrSeekToChunk(read: true, target: TChunk, offset?: number): string[] + private _readOrSeekToChunk(read: false, target: TChunk, offset?: number): void + private _readOrSeekToChunk(read: boolean, target: TChunk, offset: number = 0) { + const oldPosition = this.position; + const oldRawPosition = this.raw.position; + + let result = [...this.raw.readToChunk(target, 0)]; + this.position = this.raw.position >= oldRawPosition + ? this.position + result.length + : this.position - result.length; + + const targetPosition = this.position + offset; + if (!read) { + this.seekTo(targetPosition); + } else { + if (targetPosition >= this.position) { + // Read further until the target. + result = result.concat(this.readTo(targetPosition)); + } + else if (targetPosition >= oldPosition) { + // We passed by our target position: step back. + this.seekTo(targetPosition); + result = result.slice(0, targetPosition - oldPosition); + } else { + // The target precedes our starting position: read backwards from there. + this.seekTo(oldPosition); + result = this.readTo(targetPosition); + } + } + return result; + } + private _readOrSeekTo(read: true, target: number, roundUp?: boolean): string[]; private _readOrSeekTo(read: false, target: number, roundUp?: boolean): void; private _readOrSeekTo(read: boolean, target: number, roundUp: boolean = false): string[] | void { @@ -96,18 +145,6 @@ class _CodePointSeeker implements Seeker<string[]> { } } -export class CodePointSeeker extends _CodePointSeeker implements Seeker<string[]>, BoundaryPointer<string[]> { - constructor(public readonly raw: Seeker<string> & BoundaryPointer<Text>) { - super(raw); - } - - get referenceNode() { return [...this.raw.referenceNode.data] }; - get offsetInReferenceNode() { - const substring = this.raw.referenceNode.data.substring(0, this.raw.offsetInReferenceNode); - return [...substring].length; - }; -} - function endsWithinCharacter(s: string) { const codeUnit = s.charCodeAt(s.length - 1); return (0xD800 <= codeUnit && codeUnit <= 0xDBFF) diff --git a/packages/dom/src/seek.ts b/packages/dom/src/seek.ts index 00feaee..3832b07 100644 --- a/packages/dom/src/seek.ts +++ b/packages/dom/src/seek.ts @@ -18,7 +18,7 @@ * under the License. */ -import { Chunk, Chunker, TextNodeChunker, PartialTextNode, chunkEquals } from "./chunker"; +import { Chunk, Chunker, chunkEquals } from "./chunker"; const E_END = 'Iterator exhausted before seek ended.'; @@ -26,11 +26,6 @@ export interface NonEmptyChunker<TChunk extends Chunk<any>> extends Chunker<TChu readonly currentChunk: TChunk; } -export interface BoundaryPointer<T extends any> { - readonly referenceNode: T; - readonly offsetInReferenceNode: number; -} - export interface Seeker<T extends Iterable<any> = string> { readonly position: number; read(length?: number, roundUp?: boolean): T; @@ -39,7 +34,14 @@ export interface Seeker<T extends Iterable<any> = string> { seekTo(target: number): void; } -export class TextSeeker<TChunk extends Chunk<string>> implements Seeker<string> { +export interface ChunkSeeker<TChunk extends Chunk<any>, T extends Iterable<any> = string> extends Seeker<T> { + readonly currentChunk: TChunk; + readonly offsetInChunk: number; + seekToChunk(chunk: TChunk, offset?: number): void; + readToChunk(chunk: TChunk, offset?: number): T; +} + +export class TextSeeker<TChunk extends Chunk<string>> implements ChunkSeeker<TChunk> { // The chunk containing our current text position. get currentChunk() { return this.chunker.currentChunk; @@ -210,20 +212,3 @@ export class TextSeeker<TChunk extends Chunk<string>> implements Seeker<string> return [data, previousChunk]; } } - -export class DomSeeker extends TextSeeker<PartialTextNode> implements BoundaryPointer<Text> { - constructor(scope: Range) { - const chunker = new TextNodeChunker(scope); - if (chunker.currentChunk === null) - throw new RangeError('Range does not contain any Text nodes.'); - super(chunker as NonEmptyChunker<PartialTextNode>); - } - - get referenceNode() { - return this.currentChunk.node; - } - - get offsetInReferenceNode() { - return this.offsetInChunk + this.currentChunk.startOffset; - } -}
