-
Notifications
You must be signed in to change notification settings - Fork 1
/
textExtractionHelper.ts
126 lines (102 loc) · 3.14 KB
/
textExtractionHelper.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import type { StyleProp, TextStyle } from 'react-native';
import type { ParseShape } from './types';
type TextExtractionHelperParams = {
text: string;
patterns: ParseShape[];
};
type ParsedTextType = {
children: string;
_matched?: boolean;
style?: StyleProp<TextStyle>;
};
export const textExtractionHelper = ({
text,
patterns,
}: TextExtractionHelperParams) => {
const parse = () => {
let parsedTexts: Array<ParsedTextType> = [{ children: text }];
patterns.forEach((currentPattern) => {
const newParts: { children: string; [key: string]: any }[] = [];
const tmp = currentPattern.nonExhaustiveMaxMatchCount || 0;
const numberOfMatchesPermitted = Math.min(
Math.max(Number.isInteger(tmp) ? tmp : 0, 0) ||
Number.POSITIVE_INFINITY,
Number.POSITIVE_INFINITY
);
let currentMatches = 0;
parsedTexts.forEach((parsedText) => {
if (parsedText._matched) {
newParts.push(parsedText);
return;
}
const parts: { children: string; [key: string]: any }[] = [];
let textLeft = parsedText.children;
let indexOfMatchedString = 0;
let matches: RegExpExecArray | null;
currentPattern.pattern.lastIndex = 0;
while (textLeft && (matches = currentPattern.pattern?.exec(textLeft))) {
const previousText = textLeft.substring(0, matches.index);
indexOfMatchedString = matches.index;
if (++currentMatches > numberOfMatchesPermitted) {
break;
}
parts.push({ children: previousText });
parts.push(
getMatchedPart(
currentPattern,
matches[0],
matches,
indexOfMatchedString
)
);
textLeft = textLeft.substring(matches.index + matches[0].length);
indexOfMatchedString += matches[0].length - 1;
currentPattern.pattern.lastIndex = 0;
}
parts.push({ children: textLeft });
newParts.push(...parts);
});
parsedTexts = newParts;
});
parsedTexts.forEach((parsedText) => delete parsedText._matched);
return parsedTexts.filter((t) => !!t.children);
};
const getMatchedPart = (
matchedPattern: ParseShape,
textPart: string,
matches: RegExpExecArray,
index: number
) => {
const props: { [key: string]: any } = {};
Object.keys(matchedPattern).forEach((key) => {
if (
key === 'pattern' ||
key === 'renderText' ||
key === 'nonExhaustiveMaxMatchCount'
) {
return;
}
//@ts-ignore
if (typeof matchedPattern[key] === 'function') {
//@ts-ignore
props[key] = () => matchedPattern[key]?.(textPart, index);
} else {
//@ts-ignore
props[key] = matchedPattern[key];
}
});
let children = textPart;
if (
matchedPattern.renderText &&
typeof matchedPattern.renderText === 'function'
) {
children = matchedPattern.renderText(textPart, matches);
}
return {
...props,
children: children,
_matched: true,
};
};
return { parse, getMatchedPart };
};