Skip to content

Commit

Permalink
feat: add index selector (fixes #18)
Browse files Browse the repository at this point in the history
  • Loading branch information
gajus committed Sep 2, 2017
1 parent 899dda2 commit 5f5387b
Show file tree
Hide file tree
Showing 6 changed files with 22 additions and 17 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ A *quantifier expression* is defined using the following syntax.
|Greedy quantifier|`{n,}` where `n >= 0`|
|Greedy quantifier|`{,m}` where `m >= 1`|

A *quantifier expression* can be appended a node selector `[i]`, e.g. `{0,}[1]`. This allows to return the first node from the result set.

> If this looks familiar, its because I have adopted the syntax from regular expression language. However, unlike in regular expression, a quantifier in the context of Surgeon selector will produce an error (`SelectSubroutineUnexpectedResultCountError`) if selector result length is out of the quantifier range.

Examples:
Expand All @@ -212,8 +214,8 @@ x('select .foo {1,}');
x('select .foo {0,5}');
// Selects 1 node.
// Result is the matching element (or `null`).
x('select .foo {0,1}');
// Result is the first match in the result set (or `null`).
x('select .foo {0,}[0]');

```

Expand Down
2 changes: 1 addition & 1 deletion src/expressions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
* @see https://github.com/gajus/surgeon#quantifier-expression
* @see https://www.regex101.com/r/k3IuC4/2
*/
export const quantifierExpression = /^\{(\d+?)(,?)(-?\d+)?\}?$/;
export const quantifierExpression = /^\{(\d+?)(,?)(-?\d+)?\}(?:\[(\d+)\])?$/;
7 changes: 5 additions & 2 deletions src/parsers/parseQuantifierExpression.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import {
} from '../expressions';

type ParsedQuantifierExpressionType = {|
max: number,
min: number
+index: number | null,
+max: number,
+min: number
|};

export default (selector: string): ParsedQuantifierExpressionType => {
Expand All @@ -21,11 +22,13 @@ export default (selector: string): ParsedQuantifierExpressionType => {

if (quantifier[2] === ',') {
return {
index: quantifier[4] ? Number(quantifier[4]) : null,
max: quantifier[3] ? Number(quantifier[3]) : Infinity,
min: Number(quantifier[1])
};
} else {
return {
index: quantifier[2] ? Number(quantifier[2]) : null,
max: Number(quantifier[1]),
min: Number(quantifier[1])
};
Expand Down
16 changes: 8 additions & 8 deletions src/subroutines/selectSubroutine.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,22 @@ const createQuantifier = (quantifierExpression?: string): SelectSubroutineQuanti

if (quantifierTokens.max === 1) {
quantifier = {
index: 0,
max: 1,
min: 0,
multiple: false
min: 0
};
} else {
quantifier = {
index: quantifierTokens.index,
max: typeof quantifierTokens.max === 'undefined' ? Infinity : quantifierTokens.max,
min: typeof quantifierTokens.min === 'undefined' ? 0 : quantifierTokens.min,
multiple: true
min: typeof quantifierTokens.min === 'undefined' ? 0 : quantifierTokens.min
};
}
} else {
quantifier = {
index: 0,
max: 1,
min: 1,
multiple: false
min: 1
};
}

Expand All @@ -69,10 +69,10 @@ const selectSubroutine: SubroutineType = (subject, [cssSelector, quantifierExpre
throw new SelectSubroutineUnexpectedResultCountError(matches.length, quantifier);
}

if (quantifier.multiple === true) {
if (quantifier.index === null) {
return matches;
} else {
return matches[0] || new FinalResultSentinel(null);
return matches[quantifier.index] || new FinalResultSentinel(null);
}
};

Expand Down
4 changes: 2 additions & 2 deletions src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ export type EvaluatorType = {|
export type SubroutineType = (subject: mixed, parameters: Array<string>, bindle: Object) => mixed;

export type SelectSubroutineQuantifierType = {|
+index: number | null,
+max: number,
+min: number,
+multiple: boolean
+min: number
|};

export type UserConfigurationType = {
Expand Down
4 changes: 2 additions & 2 deletions test/surgeon/subroutines/selectSubroutine.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ test('returns a single result when expecting at most 1 result', (t): void => {
querySelectorAll
};

const result = selectSubroutine(null, ['.foo', '{0,1}'], {evaluator});
const result = selectSubroutine(null, ['.foo', '{0,1}[0]'], {evaluator});

t.true(result === 'foo');
});
Expand Down Expand Up @@ -58,7 +58,7 @@ test('returns FinalResultSentinel(null) when expecting at most 1 result', (t): v
querySelectorAll
};

const result = selectSubroutine(null, ['.foo', '{0,1}'], {evaluator});
const result = selectSubroutine(null, ['.foo', '{0,1}[0]'], {evaluator});

t.true(result instanceof FinalResultSentinel);

Expand Down

0 comments on commit 5f5387b

Please sign in to comment.