You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@annotator.apache.org by ra...@apache.org on 2020/10/05 05:31:11 UTC
[incubator-annotator] 02/02: --wip-- [skip ci]
This is an automated email from the ASF dual-hosted git repository.
randall pushed a commit to branch eliminate-extraneous-interfaces
in repository https://gitbox.apache.org/repos/asf/incubator-annotator.git
commit 372187bdd9936cfda8664ccd5b0d764ab7ef578c
Author: Randall Leeds <ra...@apache.org>
AuthorDate: Sun Sep 6 15:52:25 2020 -0700
--wip-- [skip ci]
---
packages/dom/src/range/index.ts | 23 ++++++
packages/dom/src/range/match.ts | 7 +-
packages/dom/src/range/{index.ts => selector.ts} | 6 +-
packages/selector/src/index.ts | 91 +++++++++++++++++-------
web/demo/index.js | 23 +++---
5 files changed, 103 insertions(+), 47 deletions(-)
diff --git a/packages/dom/src/range/index.ts b/packages/dom/src/range/index.ts
index 011e994..be44f30 100644
--- a/packages/dom/src/range/index.ts
+++ b/packages/dom/src/range/index.ts
@@ -18,4 +18,27 @@
* under the License.
*/
+import type { RangeSelector } from './selector';
+import { makeCreateRangeSelectorMatcher } from './match';
+
export * from './match';
+export * from './selector';
+
+export function withRange<T>(
+ createMatcher: (selector: T) => (scope: Range) => AsyncIterable<Range>,
+): (selector: T | RangeSelector<T>) => (scope: Range) => AsyncIterable<Range> {
+ const createRangeMatcher = makeCreateRangeSelectorMatcher(createMatcher);
+ return function createMatcherWithRange(selector) {
+ if ('type' in selector && selector.type === 'RangeSelector') {
+ return createRangeMatcher(selector);
+ }
+ return createMatcher(selector as T);
+ };
+}
+
+export function withRangeRecursive<T>(
+ createMatcher: (selector: T) => (scope: Range) => AsyncIterable<Range>,
+): (selector: T | RangeSelector<T>) => (scope: Range) => AsyncIterable<Range> {
+ const inner = withRange(createMatcher);
+ return withRange(inner);
+}
diff --git a/packages/dom/src/range/match.ts b/packages/dom/src/range/match.ts
index bc2d601..cfa297b 100644
--- a/packages/dom/src/range/match.ts
+++ b/packages/dom/src/range/match.ts
@@ -19,12 +19,7 @@
*/
import { product } from './cartesian';
-
-export interface RangeSelector<T> {
- type: 'RangeSelector';
- startSelector: T;
- endSelector: T;
-}
+import type { RangeSelector } from './selector';
export function makeCreateRangeSelectorMatcher<T>(
createMatcher: (selector: T) => (scope: Range) => AsyncIterable<Range>,
diff --git a/packages/dom/src/range/index.ts b/packages/dom/src/range/selector.ts
similarity index 89%
copy from packages/dom/src/range/index.ts
copy to packages/dom/src/range/selector.ts
index 011e994..7690e76 100644
--- a/packages/dom/src/range/index.ts
+++ b/packages/dom/src/range/selector.ts
@@ -18,4 +18,8 @@
* under the License.
*/
-export * from './match';
+export interface RangeSelector<T> {
+ type: 'RangeSelector';
+ startSelector: T;
+ endSelector: T;
+}
diff --git a/packages/selector/src/index.ts b/packages/selector/src/index.ts
index 99c2840..deca7b1 100644
--- a/packages/selector/src/index.ts
+++ b/packages/selector/src/index.ts
@@ -18,46 +18,83 @@
* under the License.
*/
+type Selector = { type: string };
+
+type Matcher<TScope, TMatch> = (scope: TScope) => AsyncIterable<TMatch>;
+type MatcherCreator<TSelector extends Selector, TScope, TMatch> = (
+ selector: TSelector,
+) => Matcher<TScope, TMatch>;
+
+type Plugin<TSelector extends Selector, TScope, TMatch> = (
+ next: MatcherCreator<TSelector, TScope, TMatch>,
+ recurse: MatcherCreator<TSelector, TScope, TMatch>,
+) => MatcherCreator<TSelector, TScope, TMatch>;
+
+export function composeMatcherCreator<
+ TSelector extends Selector,
+ TScope,
+ TMatch extends TScope
+>(
+ ...plugins: Array<Plugin<TSelector, TScope, TMatch>>
+): MatcherCreator<TSelector, TScope, TMatch> {
+ function innerMatcherCreator(selector: TSelector): Matcher<TScope, TMatch> {
+ throw new TypeError(`Unhandled selector. Selector type: ${selector.type}`);
+ }
+
+ function outerMatcherCreator(selector: TSelector): Matcher<TScope, TMatch> {
+ return composedMatcherCreator(selector);
+ }
+
+ const composedMatcherCreator = plugins.reduceRight(
+ (matcherCreator, plugin) => plugin(matcherCreator, outerMatcherCreator),
+ innerMatcherCreator,
+ );
+
+ return outerMatcherCreator;
+}
+
+type MatcherCreatorMap<TSelector extends Selector, TScope, TMatch> = {
+ [K: string]: MatcherCreator<TSelector, TScope, TMatch>;
+};
+
+export function mapSelectorTypes<TSelector extends Selector, TScope, TMatch>(
+ matcherCreators: MatcherCreatorMap<TSelector, TScope, TMatch>,
+): Plugin<TSelector, TScope, TMatch> {
+ return function mapSelectorTypesPlugin(next) {
+ return function (selector) {
+ const matcherCreator = matcherCreators[selector.type];
+
+ if (matcherCreator) {
+ return matcherCreator(selector);
+ }
+
+ return next(selector);
+ };
+ };
+}
+
export function withRefinement<
- TSelector,
- TSelectorScope,
- TRefinement,
- TRefinementScope,
+ TSelector extends Selector & { refinedBy?: TSelector },
+ TScope,
TMatch
>(
- createMatcher: (
- selector: TSelector,
- ) => (scope: TSelectorScope) => AsyncIterable<TRefinementScope>,
- createRefiner: (
- selector: TRefinement,
- ) => (scope: TRefinementScope) => AsyncIterable<TMatch>,
-): (
- selector: TSelector & { refinedBy?: TRefinement },
-) => (scope: TSelectorScope) => AsyncIterable<TRefinementScope | TMatch> {
+ next: (selector: TSelector) => (scope: TScope) => AsyncIterable<TScope>,
+ recurse: (selector: TSelector) => (scope: TScope) => AsyncIterable<TMatch>,
+): MatcherCreator<TSelector, TScope, TMatch> {
return function createMatcherWithRefinement(selector) {
const { refinedBy } = selector;
+ const matcher = next(selector);
if (refinedBy) {
- const match = createMatcher(selector);
- const refine = createRefiner(refinedBy);
+ const refine = recurse(refinedBy);
return async function* matchAll(scope) {
- for await (const subScope of match(scope)) {
+ for await (const subScope of matcher(scope)) {
yield* refine(subScope);
}
};
}
- return createMatcher(selector);
+ return matcher;
};
}
-
-export function withRecursiveRefinement<TSelector, TScope>(
- createMatcher: (
- selector: TSelector & { refinedBy?: TSelector },
- ) => (scope: TScope) => AsyncIterable<TScope>,
-): (
- selector: TSelector & { refinedBy?: TSelector },
-) => (scope: TScope) => AsyncIterable<TScope> {
- return withRefinement(createMatcher, createMatcher);
-}
diff --git a/web/demo/index.js b/web/demo/index.js
index 18c9983..a7f2816 100644
--- a/web/demo/index.js
+++ b/web/demo/index.js
@@ -21,12 +21,15 @@
/* global info, module, source, target */
import {
- makeCreateRangeSelectorMatcher,
createTextQuoteSelectorMatcher,
describeTextQuote,
highlightRange,
} from '@annotator/dom';
-import { withRecursiveRefinement } from '@annotator/selector';
+import {
+ composeMatcherCreator,
+ mapSelectorTypes,
+ withRefinement,
+} from '@annotator/selector';
const EXAMPLE_SELECTORS = [
{
@@ -91,18 +94,12 @@ function cleanup() {
target.normalize();
}
-const createMatcher = withRecursiveRefinement((selector) => {
- const innerCreateMatcher = {
+const createMatcher = composeMatcherCreator(
+ withRefinement,
+ mapSelectorTypes({
TextQuoteSelector: createTextQuoteSelectorMatcher,
- RangeSelector: makeCreateRangeSelectorMatcher(createMatcher),
- }[selector.type];
-
- if (!innerCreateMatcher) {
- throw new Error(`Unsupported selector type: ${selector.type}`);
- }
-
- return innerCreateMatcher(selector);
-});
+ }),
+);
async function anchor(selector) {
const scope = document.createRange();