-
Notifications
You must be signed in to change notification settings - Fork 163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Nested Matchers #81
Comments
I have a pretty simple use case, but require nested matching nonetheless. After playing around with import React, { FunctionComponent, useState } from 'react';
import { StyleProp, Text, TextProps, TextStyle } from 'react-native';
interface P9TextParserConfig {
pattern: RegExp;
style?: StyleProp<TextStyle>;
transform?(text: string): string;
}
interface P9TextParseResult {
match: string;
style?: StyleProp<TextStyle>;
}
export interface P9ParsedTextProps extends TextProps {
parsers: P9TextParserConfig[];
}
export const P9ParsedText: FunctionComponent<P9ParsedTextProps> = ({
children,
parsers,
style: rootStyle,
...rest
}) => {
const [parsed] = useState(parseText([{ match: children as string }], parsers, 0, []));
return (
<>
<Text {...rest} style={rootStyle}>
{parsed.map(({ match, style }, index) =>
style ? (
<Text key={index.toString()} style={[rootStyle, style]}>
{match}
</Text>
) : (
match
)
)}
</Text>
</>
);
};
function parseText(
input: P9TextParseResult[],
parsers: P9TextParserConfig[],
current: number,
matches: P9TextParseResult[]
): P9TextParseResult[] {
if (current === parsers.length) {
return matches;
}
const { pattern, style, transform } = parsers[current];
matches = input
.map(({ match: parent, style: parentStyle }) =>
parent
.split(pattern)
.map((match) =>
pattern.test(match) ? { match: transform ? transform(match) : match, style } : { match, style: parentStyle }
)
)
.reduce((output, next) => output.concat(next), []);
return parseText(matches, parsers, ++current, matches);
} |
@sellmeadog -- this is great! Were you proposing this as a way to rewrite this library? or just as an example on how to do it if people need this feature? A. I'm just the steward of react-native-parsed-text, not the originator, so I'm probably not going to completely rewrite this library. |
It's not a strict proposal, I was just trying to showcase what worked for me and see what value it might add to the overall discussion. I think it could be the base of a rewrite, but |
Can I propose a simple way to fix this would be to allow
This avoid complicating the parsing logic and moves the recursion onto the caller. |
The current Architecture doesn't really support nested matchers. It says if you match a thing, then the hunk of text that was matched is no longer eligible to be matched by something else.
And unfortunately this is order dependent so you should put your important patterns as early as possible in the pattern list.
Example:
[b]Bold[/b] [i]italic [b]italic and bold[/b][/i]
[ { …bold }, { …italic } ]
then with this example, you'll get the bold stuff set right, but you won't get any italic code.[ { …italic }, { …bold } ]
then with this example text, you'll get the first bold section, and you'll get the rest in italicsWorkaround
There could be a workaround for some, but not all use-cases, but this only works if you delete the tokens you're matching against -- so you can't keep the tokens in the original stream.
The workaround is recursive and a bit hard to describe:
[ { …boldStartPattern, tag: "boldStart" }, [ { …boldEndPattern, tag: "boldEnd", }, { …italicStartPattern, tag: "italicStart" }, { …italicEndPattern, tag: "italicEnd" } ]
Then you use TextExtraction directly, and recursively from within the "renderText" methods. Each unique renderText method deletes the Tag/Tokens they're matching against, and repeats the extraction recursively while mutating a global array of matched chunks / unmatched chunks.
Your result will be a deeply nested array of arrays, something like this:
While this isn't pretty, and wouldn't help for mismatched tags, it would probably be functional for rendering into a real UI.
Discussion
If people are interested in this feature, please mark your interests on this ticket or in comments, and let's discuss it.
The text was updated successfully, but these errors were encountered: