clarity-pattern-parser 11.3.10 → 11.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.yarn/install-state.gz +0 -0
- package/dist/ast/Node.test.d.ts +1 -0
- package/dist/grammar/Grammar.test.d.ts +1 -0
- package/dist/grammar/patterns.test.d.ts +1 -0
- package/dist/index.browser.js +196 -113
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +196 -113
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +196 -113
- package/dist/index.js.map +1 -1
- package/dist/intellisense/AutoComplete.d.ts +26 -8
- package/dist/intellisense/AutoComplete.test.d.ts +2 -0
- package/dist/intellisense/SuggestionOption.d.ts +18 -2
- package/dist/intellisense/css/cssValue.d.ts +3 -0
- package/dist/intellisense/css/divider.d.ts +3 -0
- package/dist/intellisense/css/hex.d.ts +3 -0
- package/dist/intellisense/css/method.d.ts +3 -0
- package/dist/intellisense/css/name.d.ts +3 -0
- package/dist/intellisense/css/number.d.ts +3 -0
- package/dist/intellisense/css/optionalSpaces.d.ts +2 -0
- package/dist/intellisense/css/spaces.d.ts +3 -0
- package/dist/intellisense/css/unit.d.ts +3 -0
- package/dist/intellisense/css/value.d.ts +3 -0
- package/dist/intellisense/css/values.d.ts +3 -0
- package/dist/intellisense/javascript/Javascript.test.d.ts +1 -0
- package/dist/intellisense/javascript/arrayLiteral.d.ts +2 -0
- package/dist/intellisense/javascript/assignment.d.ts +3 -0
- package/dist/intellisense/javascript/deleteStatement.d.ts +2 -0
- package/dist/intellisense/javascript/escapedCharacter.d.ts +3 -0
- package/dist/intellisense/javascript/exponent.d.ts +3 -0
- package/dist/intellisense/javascript/expression.d.ts +3 -0
- package/dist/intellisense/javascript/fraction.d.ts +3 -0
- package/dist/intellisense/javascript/infixOperator.d.ts +3 -0
- package/dist/intellisense/javascript/integer.d.ts +3 -0
- package/dist/intellisense/javascript/invocation.d.ts +2 -0
- package/dist/intellisense/javascript/keywords.d.ts +2 -0
- package/dist/intellisense/javascript/literal.d.ts +3 -0
- package/dist/intellisense/javascript/name.d.ts +2 -0
- package/dist/intellisense/javascript/numberLiteral.d.ts +2 -0
- package/dist/intellisense/javascript/objectAccess.d.ts +2 -0
- package/dist/intellisense/javascript/objectLiteral.d.ts +3 -0
- package/dist/intellisense/javascript/optionalSpaces.d.ts +2 -0
- package/dist/intellisense/javascript/parameters.d.ts +3 -0
- package/dist/intellisense/javascript/prefixOperator.d.ts +3 -0
- package/dist/intellisense/javascript/propertyAccess.d.ts +3 -0
- package/dist/intellisense/javascript/stringLiteral.d.ts +3 -0
- package/dist/patterns/Context.test.d.ts +1 -0
- package/dist/patterns/Cursor.test.d.ts +1 -0
- package/dist/patterns/CursorHistory.test.d.ts +1 -0
- package/dist/patterns/Expression.test.d.ts +1 -0
- package/dist/patterns/FiniteRepeat.test.d.ts +1 -0
- package/dist/patterns/InfiniteRepeat.test.d.ts +1 -0
- package/dist/patterns/Literal.test.d.ts +1 -0
- package/dist/patterns/Not.test.d.ts +1 -0
- package/dist/patterns/Optional.test.d.ts +1 -0
- package/dist/patterns/Options.test.d.ts +1 -0
- package/dist/patterns/PrecedenceTree.test.d.ts +1 -0
- package/dist/patterns/Reference.test.d.ts +1 -0
- package/dist/patterns/Regex.test.d.ts +1 -0
- package/dist/patterns/Repeat.test.d.ts +1 -0
- package/dist/patterns/Sequence.test.d.ts +1 -0
- package/dist/patterns/TakeUntil.test.d.ts +1 -0
- package/dist/query/query.test.d.ts +1 -0
- package/dist/query/selector.test.d.ts +1 -0
- package/jest.config.js +3 -1
- package/jest.coverage.config.js +2 -0
- package/package.json +7 -6
- package/src/grammar/Grammar.test.ts +1 -0
- package/src/grammar/Grammar.ts +2 -0
- package/src/grammar/patterns.test.ts +36 -0
- package/src/grammar/spec.md +233 -84
- package/src/intellisense/AutoComplete.test.ts +627 -158
- package/src/intellisense/AutoComplete.ts +233 -110
- package/src/intellisense/SuggestionOption.ts +25 -2
- package/src/intellisense/css/optionalSpaces.ts +1 -1
- package/src/intellisense/css/value.ts +1 -1
- package/src/patterns/Expression.ts +4 -2
- package/src/patterns/Literal.test.ts +1 -1
- package/src/patterns/Options.test.ts +1 -1
- package/src/patterns/PrecedenceTree.ts +31 -7
- package/src/patterns/Reference.test.ts +1 -1
- package/src/patterns/Regex.test.ts +3 -3
- package/src/patterns/Sequence.test.ts +1 -1
- package/tsconfig.json +1 -1
- package/dist/Cursor.d.ts +0 -37
- package/dist/CursorHistory.d.ts +0 -27
- package/dist/TextSuggester.d.ts +0 -48
- package/dist/ast/Visitor.d.ts +0 -31
- package/dist/grammar/patterns/andLiteral.d.ts +0 -4
- package/dist/grammar/patterns/inlinePattern.d.ts +0 -1
- package/dist/grammar/patterns/orLiteral.d.ts +0 -2
- package/dist/patterns/And.d.ts +0 -41
- package/dist/patterns/DepthCache.d.ts +0 -6
- package/dist/patterns/ExpressionPattern.d.ts +0 -66
- package/dist/patterns/LookAhead.d.ts +0 -8
- package/dist/patterns/Or.d.ts +0 -36
- package/dist/patterns/Recursive.d.ts +0 -12
- package/dist/patterns/RightAssociatedPattern.d.ts +0 -31
- package/dist/patterns/arePatternsEqual.d.ts +0 -2
- package/dist/patterns/getNextPattern.d.ts +0 -2
- package/dist/types.d.ts +0 -797
- package/src/generator/generator.test.ts +0 -27
- package/src/generator/generator.ts +0 -103
- package/src/generator/igenerator.ts +0 -6
- package/src/generator/ivisitor.ts +0 -23
- package/src/generator/typescriptVisitor.ts +0 -158
|
@@ -3,7 +3,8 @@ import { Match } from "../patterns/CursorHistory";
|
|
|
3
3
|
import { ParseError } from "../patterns/ParseError";
|
|
4
4
|
import { Pattern } from "../patterns/Pattern";
|
|
5
5
|
import { Suggestion } from "./Suggestion";
|
|
6
|
-
import { SuggestionOption } from "./SuggestionOption";
|
|
6
|
+
import { SuggestionSegment, SuggestionOption, CompositeSuggestion } from "./SuggestionOption";
|
|
7
|
+
|
|
7
8
|
|
|
8
9
|
export interface AutoCompleteOptions {
|
|
9
10
|
/**
|
|
@@ -17,6 +18,12 @@ export interface AutoCompleteOptions {
|
|
|
17
18
|
* and the string array are the tokens suggested for that pattern.
|
|
18
19
|
*/
|
|
19
20
|
customTokens?: Record<string, string[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Suggestions may share the same text but differ in their suggestionSequence.
|
|
23
|
+
* By default, duplicates are removed and only the first instance is kept.
|
|
24
|
+
* Disabling deduplication allows all distinct instances to be returned together.
|
|
25
|
+
*/
|
|
26
|
+
disableDedupe?: boolean;
|
|
20
27
|
}
|
|
21
28
|
|
|
22
29
|
const defaultOptions = { greedyPatternNames: [], customTokens: {} };
|
|
@@ -33,6 +40,10 @@ export class AutoComplete {
|
|
|
33
40
|
this._text = "";
|
|
34
41
|
}
|
|
35
42
|
|
|
43
|
+
suggestFor(text: string): Suggestion {
|
|
44
|
+
return this.suggestForWithCursor(new Cursor(text));
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
suggestForWithCursor(cursor: Cursor): Suggestion {
|
|
37
48
|
cursor.moveTo(0);
|
|
38
49
|
|
|
@@ -41,14 +52,17 @@ export class AutoComplete {
|
|
|
41
52
|
this._cursor.startRecording();
|
|
42
53
|
|
|
43
54
|
if (cursor.length === 0) {
|
|
44
|
-
|
|
55
|
+
|
|
56
|
+
const suggestion: Suggestion = {
|
|
45
57
|
isComplete: false,
|
|
46
|
-
options: this.
|
|
58
|
+
options: this._createSuggestionOptionsFromMatch(),
|
|
47
59
|
error: new ParseError(0, 0, this._pattern),
|
|
48
60
|
errorAtIndex: 0,
|
|
49
61
|
cursor,
|
|
50
62
|
ast: null
|
|
51
|
-
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return suggestion;
|
|
52
66
|
}
|
|
53
67
|
|
|
54
68
|
let errorAtIndex = null;
|
|
@@ -56,7 +70,7 @@ export class AutoComplete {
|
|
|
56
70
|
|
|
57
71
|
const ast = this._pattern.parse(this._cursor);
|
|
58
72
|
const isComplete = ast?.value === this._text;
|
|
59
|
-
const options = this.
|
|
73
|
+
const options = this._getAllSuggestionsOptions();
|
|
60
74
|
|
|
61
75
|
if (!isComplete && options.length > 0 && !this._cursor.hasError) {
|
|
62
76
|
const startIndex = options.reduce((lowestIndex, o) => {
|
|
@@ -111,161 +125,269 @@ export class AutoComplete {
|
|
|
111
125
|
return 0;
|
|
112
126
|
}
|
|
113
127
|
|
|
114
|
-
suggestFor(text: string): Suggestion {
|
|
115
|
-
return this.suggestForWithCursor(new Cursor(text));
|
|
116
|
-
}
|
|
117
128
|
|
|
118
|
-
private _getAllOptions() {
|
|
119
|
-
const errorMatches = this._getOptionsFromErrors();
|
|
120
|
-
const leafMatches = this._cursor.leafMatches.map((m) => this._createSuggestionsFromMatch(m)).flat();
|
|
121
|
-
const finalResults: SuggestionOption[] = [];
|
|
122
129
|
|
|
123
|
-
|
|
130
|
+
private _getAllSuggestionsOptions() {
|
|
131
|
+
|
|
132
|
+
const errorMatchOptions = this._createSuggestionOptionsFromErrors();
|
|
133
|
+
const leafMatchOptions = this._cursor.leafMatches.map((m) => this._createSuggestionOptionsFromMatch(m)).flat();
|
|
134
|
+
|
|
135
|
+
const finalResults: SuggestionOption[] = [];
|
|
136
|
+
[...leafMatchOptions, ...errorMatchOptions].forEach(m => {
|
|
124
137
|
const index = finalResults.findIndex(f => m.text === f.text);
|
|
125
138
|
if (index === -1) {
|
|
126
139
|
finalResults.push(m);
|
|
127
140
|
}
|
|
128
141
|
});
|
|
129
142
|
|
|
130
|
-
return finalResults;
|
|
143
|
+
return getFurthestOptions(finalResults);
|
|
131
144
|
}
|
|
132
145
|
|
|
133
|
-
private
|
|
146
|
+
private _createSuggestionOptionsFromErrors() {
|
|
134
147
|
// These errored because the length of the string.
|
|
135
148
|
const errors = this._cursor.errors.filter(e => e.lastIndex === this._cursor.length);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const adjustedTokens: Set<string> = new Set();
|
|
139
|
-
const currentText = this._cursor.getChars(e.startIndex, e.lastIndex);
|
|
140
|
-
|
|
141
|
-
tokens.forEach((token)=>{
|
|
142
|
-
if (token.startsWith(currentText) && token.length > currentText.length){
|
|
143
|
-
const difference = token.length - currentText.length;
|
|
144
|
-
const suggestedText = token.slice(-difference);
|
|
145
|
-
adjustedTokens.add(suggestedText);
|
|
146
|
-
}
|
|
147
|
-
});
|
|
149
|
+
|
|
150
|
+
const errorSuggestionOptions = errors.map(parseError => {
|
|
148
151
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
});
|
|
152
|
+
const currentText = this._cursor.getChars(parseError.startIndex, parseError.lastIndex);
|
|
153
|
+
|
|
154
|
+
const compositeSuggestions = this._getCompositeSuggestionsForPattern(parseError.pattern);
|
|
155
|
+
const trimmedErrorCompositeSuggestions = this._trimSuggestionsByExistingText(currentText, compositeSuggestions);
|
|
156
|
+
|
|
157
|
+
return this._createSuggestions(parseError.lastIndex, trimmedErrorCompositeSuggestions);
|
|
158
|
+
}).flat();
|
|
159
|
+
|
|
160
|
+
const dedupedErrorSuggestionOptions = this._deDupeCompositeSuggestions(errorSuggestionOptions);
|
|
156
161
|
|
|
157
|
-
return
|
|
162
|
+
return dedupedErrorSuggestionOptions
|
|
158
163
|
}
|
|
159
164
|
|
|
160
|
-
private _createSuggestionsFromRoot(): SuggestionOption[] {
|
|
161
|
-
const suggestions: SuggestionOption[] = [];
|
|
162
|
-
const tokens = [...this._pattern.getTokens(),... this._getTokensForPattern(this._pattern)];
|
|
163
165
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
private _createSuggestionOptionsFromMatch(match?: Match): SuggestionOption[] {
|
|
167
|
+
|
|
168
|
+
if (match?.pattern == null) {
|
|
169
|
+
const compositeSuggestions = this._getCompositeSuggestionsForPattern(this._pattern);
|
|
170
|
+
return this._createSuggestions(-1, compositeSuggestions);
|
|
168
171
|
}
|
|
169
172
|
|
|
170
|
-
|
|
171
|
-
|
|
173
|
+
if ( match?.node != null) {
|
|
174
|
+
const currentText = this._text.slice(match.node.startIndex,match.node.endIndex)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
/**Captures suggestions for a "completed" match pattern that still has existing possible suggestions.
|
|
178
|
+
* particularly relevant when working with set/custom tokens.
|
|
179
|
+
*/
|
|
180
|
+
const matchCompositeSuggestions = this._getCompositeSuggestionsForPattern(match.pattern)
|
|
181
|
+
const trimmedMatchCompositeSuggestions = this._trimSuggestionsByExistingText(currentText, matchCompositeSuggestions)
|
|
172
182
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
183
|
+
|
|
184
|
+
const leafPatterns = match.pattern.getNextPatterns();
|
|
185
|
+
const leafCompositeSuggestions = leafPatterns.flatMap(leafPattern =>
|
|
186
|
+
this._getCompositeSuggestionsForPattern(leafPattern)
|
|
187
|
+
);
|
|
177
188
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
* - IE. **currentText:** *abc*, **baseToken:** *abcdef*, **trailingToken:** *def*
|
|
184
|
-
*/
|
|
185
|
-
const trailingTokens = currentPatternsTokens.reduce<string[]>((acc, token) => {
|
|
186
|
-
if (token.startsWith(textStartingMatch)) {
|
|
187
|
-
const sliced = token.slice(textStartingMatch.length);
|
|
188
|
-
if (sliced !== '') {
|
|
189
|
-
acc.push(sliced);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return acc;
|
|
193
|
-
}, []);
|
|
194
|
-
|
|
195
|
-
const leafPatterns = match.pattern.getNextPatterns();
|
|
196
|
-
const leafTokens = leafPatterns.reduce((acc: string[], leafPattern) => {
|
|
197
|
-
acc.push(...this._getTokensForPattern(leafPattern));
|
|
198
|
-
return acc;
|
|
199
|
-
}, []);
|
|
200
|
-
|
|
201
|
-
const allTokens = [...trailingTokens,...leafTokens]
|
|
202
|
-
return this._createSuggestions(match.node.lastIndex, allTokens);
|
|
189
|
+
const allCompositeSuggestions = [...leafCompositeSuggestions,...trimmedMatchCompositeSuggestions,]
|
|
190
|
+
|
|
191
|
+
const dedupedCompositeSuggestions = this._deDupeCompositeSuggestions(allCompositeSuggestions);
|
|
192
|
+
|
|
193
|
+
return this._createSuggestions(match.node.lastIndex, dedupedCompositeSuggestions);
|
|
203
194
|
} else {
|
|
204
195
|
return [];
|
|
205
196
|
}
|
|
206
197
|
}
|
|
207
198
|
|
|
208
|
-
|
|
209
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Compares suggestions with provided text and removes completed sub-sequences and preceding text
|
|
201
|
+
* - IE. **currentText:** *abc*, **sequence:** *[{ab}{cd}{ef}*
|
|
202
|
+
* - refines to {d}{ef}
|
|
203
|
+
*/
|
|
204
|
+
private _trimSuggestionsByExistingText(currentText: string, compositeSuggestions: CompositeSuggestion[]): CompositeSuggestion[] {
|
|
205
|
+
|
|
206
|
+
const trimmedSuggestions = compositeSuggestions.reduce<CompositeSuggestion[]>((acc, compositeSuggestion) => {
|
|
207
|
+
if (compositeSuggestion.text.startsWith(currentText)) {
|
|
208
|
+
|
|
209
|
+
const filteredSegments = this._filterCompletedSubSegments(currentText, compositeSuggestion);
|
|
210
|
+
const slicedSuggestionText = compositeSuggestion.text.slice(currentText.length);
|
|
211
|
+
|
|
212
|
+
if (slicedSuggestionText !== '') {
|
|
213
|
+
const refinedCompositeSuggestion: CompositeSuggestion = {
|
|
214
|
+
text: slicedSuggestionText,
|
|
215
|
+
suggestionSequence: filteredSegments,
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
acc.push(refinedCompositeSuggestion);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return acc;
|
|
222
|
+
}, []);
|
|
223
|
+
|
|
224
|
+
return trimmedSuggestions
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Removed segments already accounted for in the existing text.
|
|
228
|
+
* ie. sequence pattern segments ≈ [{look}, {an example}, {phrase}]
|
|
229
|
+
* fullText = "look an"
|
|
230
|
+
* remove {look} segment as its already been completed by the existing text.
|
|
231
|
+
*/
|
|
232
|
+
private _filterCompletedSubSegments(currentText: string, compositeSuggestion: CompositeSuggestion){
|
|
233
|
+
|
|
234
|
+
let elementsToRemove: SuggestionSegment [] = [];
|
|
235
|
+
let workingText = currentText;
|
|
236
|
+
|
|
237
|
+
compositeSuggestion.suggestionSequence.forEach(segment => {
|
|
238
|
+
/**sub segment has been completed, remove it from the sequence */
|
|
239
|
+
if(workingText.startsWith(segment.text)){
|
|
240
|
+
workingText = workingText.slice(segment.text.length);
|
|
241
|
+
elementsToRemove.push (segment);
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
const filteredSegments = compositeSuggestion.suggestionSequence.filter(segment => !elementsToRemove.includes (segment) );
|
|
246
|
+
|
|
247
|
+
return filteredSegments
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private _getCompositeSuggestionsForPattern(pattern: Pattern):CompositeSuggestion[] {
|
|
251
|
+
|
|
252
|
+
const suggestionsToReturn:CompositeSuggestion[] = [];
|
|
253
|
+
|
|
254
|
+
const leafPatterns = pattern.getPatterns();
|
|
255
|
+
// for when pattern has no leafPatterns and only returns itself
|
|
256
|
+
if(leafPatterns.length === 1 && leafPatterns[0].id === pattern.id) {
|
|
257
|
+
|
|
258
|
+
const currentCustomTokens = this._getCustomTokens(pattern);
|
|
259
|
+
const currentTokens = pattern.getTokens();
|
|
260
|
+
const allTokens = [...currentCustomTokens, ...currentTokens];
|
|
261
|
+
|
|
262
|
+
const leafCompositeSuggestions: CompositeSuggestion[] = allTokens.map(token => {
|
|
263
|
+
|
|
264
|
+
const segment:SuggestionSegment = {
|
|
265
|
+
text: token,
|
|
266
|
+
pattern: pattern,
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const compositeSuggestion:CompositeSuggestion = {
|
|
270
|
+
text: token,
|
|
271
|
+
suggestionSequence: [segment],
|
|
272
|
+
}
|
|
273
|
+
return compositeSuggestion;
|
|
274
|
+
})
|
|
275
|
+
suggestionsToReturn.push(...leafCompositeSuggestions);
|
|
276
|
+
|
|
277
|
+
}else{
|
|
278
|
+
|
|
279
|
+
const currentCustomTokens = this._getCustomTokens(pattern);
|
|
280
|
+
|
|
281
|
+
const patternsSuggestionList = currentCustomTokens.map(token => {
|
|
282
|
+
const segment:SuggestionSegment = {
|
|
283
|
+
text: token,
|
|
284
|
+
pattern: pattern,
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const patternSuggestion:CompositeSuggestion = {
|
|
288
|
+
text: token,
|
|
289
|
+
suggestionSequence: [segment],
|
|
290
|
+
}
|
|
291
|
+
return patternSuggestion;
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
const leafCompositeSuggestions = leafPatterns.map(lp => this._getCompositeSuggestionsForPattern(lp)).flat();
|
|
295
|
+
|
|
296
|
+
suggestionsToReturn.push(...patternsSuggestionList, ...leafCompositeSuggestions);
|
|
297
|
+
}
|
|
210
298
|
|
|
211
299
|
if (this._options.greedyPatternNames != null && this._options.greedyPatternNames.includes(pattern.name)) {
|
|
300
|
+
|
|
212
301
|
const nextPatterns = pattern.getNextPatterns();
|
|
213
|
-
|
|
214
|
-
|
|
302
|
+
|
|
303
|
+
const nextPatternedTokensList = nextPatterns.reduce<CompositeSuggestion[]>((acc, pattern) => {
|
|
304
|
+
const patternedTokensList = this._getCompositeSuggestionsForPattern(pattern);
|
|
305
|
+
acc.push(...patternedTokensList);
|
|
306
|
+
|
|
215
307
|
return acc;
|
|
216
308
|
}, []);
|
|
217
|
-
// using set to prevent duplicates
|
|
218
|
-
const tokens = new Set<string>();
|
|
219
309
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
310
|
+
const compositeSuggestionList:CompositeSuggestion[] = [];
|
|
311
|
+
|
|
312
|
+
for (const currentSuggestion of suggestionsToReturn) {
|
|
313
|
+
for (const nextSuggestionWithSubElements of nextPatternedTokensList) {
|
|
314
|
+
|
|
315
|
+
const augmentedTokenWithPattern:CompositeSuggestion = {
|
|
316
|
+
text: currentSuggestion.text + nextSuggestionWithSubElements.text,
|
|
317
|
+
suggestionSequence: [...currentSuggestion.suggestionSequence,...nextSuggestionWithSubElements.suggestionSequence ],
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
compositeSuggestionList.push(augmentedTokenWithPattern);
|
|
223
321
|
}
|
|
224
322
|
}
|
|
225
323
|
|
|
226
|
-
return
|
|
227
|
-
|
|
228
|
-
|
|
324
|
+
return compositeSuggestionList;
|
|
325
|
+
|
|
326
|
+
} else {
|
|
327
|
+
|
|
328
|
+
const dedupedSuggestions = this._deDupeCompositeSuggestions(suggestionsToReturn);
|
|
329
|
+
return dedupedSuggestions;
|
|
229
330
|
}
|
|
230
331
|
}
|
|
231
332
|
|
|
232
|
-
private
|
|
233
|
-
const customTokensMap: any = this._options.customTokens || {};
|
|
234
|
-
const leafPatterns = pattern.getPatterns();
|
|
333
|
+
private _getCustomTokens(pattern: Pattern) {
|
|
235
334
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
* - prevent mutation of original customTokensMap
|
|
239
|
-
*/
|
|
240
|
-
const customTokensForPattern = new Set<string>(customTokensMap[pattern.name] ?? []);
|
|
335
|
+
const customTokensMap: Record<string, string[]> = this._options.customTokens || {};
|
|
336
|
+
const customTokens = customTokensMap[pattern.name] ?? [];
|
|
241
337
|
|
|
242
|
-
|
|
243
|
-
const augmentedTokens = customTokensMap[lp.name] ?? [];
|
|
244
|
-
const lpsCombinedTokens = [...lp.getTokens(), ...augmentedTokens];
|
|
338
|
+
const allTokens = [...customTokens];
|
|
245
339
|
|
|
246
|
-
|
|
247
|
-
customTokensForPattern.add(token);
|
|
340
|
+
return allTokens;
|
|
248
341
|
}
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
private _deDupeCompositeSuggestions<T extends CompositeSuggestion>(suggestions: T[]): T[] {
|
|
346
|
+
|
|
347
|
+
if (this._options.disableDedupe) {
|
|
348
|
+
return suggestions;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const seen = new Set<string>();
|
|
352
|
+
const unique: T[] = [];
|
|
353
|
+
|
|
354
|
+
for (const suggestion of suggestions) {
|
|
355
|
+
// Create a unique key based on text and subElements
|
|
356
|
+
const subElementsKey = suggestion.suggestionSequence
|
|
357
|
+
.map(sub => ` ${sub.pattern.name} - ${sub.text} `)
|
|
358
|
+
.sort()
|
|
359
|
+
.join('|');
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
const key = `${suggestion.text}|${subElementsKey}`;
|
|
363
|
+
|
|
364
|
+
if (!seen.has(key)) {
|
|
365
|
+
seen.add(key);
|
|
366
|
+
unique.push(suggestion);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return unique;
|
|
249
371
|
}
|
|
250
|
-
return [...customTokensForPattern];
|
|
251
|
-
}
|
|
252
372
|
|
|
253
|
-
private _createSuggestions(lastIndex: number,
|
|
373
|
+
private _createSuggestions(lastIndex: number, compositeSuggestionList:CompositeSuggestion[]): SuggestionOption[] {
|
|
374
|
+
|
|
254
375
|
let textToIndex = lastIndex === -1 ? "" : this._cursor.getChars(0, lastIndex);
|
|
255
376
|
const suggestionStrings: string[] = [];
|
|
256
377
|
const options: SuggestionOption[] = [];
|
|
257
378
|
|
|
258
|
-
for (const
|
|
379
|
+
for (const compositeSuggestion of compositeSuggestionList) {
|
|
259
380
|
// concatenated for start index identification inside createSuggestion
|
|
260
|
-
const
|
|
261
|
-
const alreadyExist = suggestionStrings.includes(suggestion);
|
|
262
|
-
const isSameAsText = suggestion === this._text;
|
|
381
|
+
const existingTextWithSuggestion = textToIndex + compositeSuggestion.text;
|
|
263
382
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
383
|
+
const alreadyExist = suggestionStrings.includes(existingTextWithSuggestion);
|
|
384
|
+
const isSameAsText = existingTextWithSuggestion === this._text;
|
|
385
|
+
|
|
386
|
+
// if ( !alreadyExist && !isSameAsText) {
|
|
387
|
+
suggestionStrings.push(existingTextWithSuggestion);
|
|
388
|
+
const suggestionOption = this._createSuggestionOption(this._cursor.text, existingTextWithSuggestion, compositeSuggestion.suggestionSequence);
|
|
267
389
|
options.push(suggestionOption);
|
|
268
|
-
}
|
|
390
|
+
// }
|
|
269
391
|
}
|
|
270
392
|
|
|
271
393
|
const reducedOptions = getFurthestOptions(options);
|
|
@@ -274,13 +396,14 @@ export class AutoComplete {
|
|
|
274
396
|
return reducedOptions;
|
|
275
397
|
}
|
|
276
398
|
|
|
277
|
-
private
|
|
399
|
+
private _createSuggestionOption(fullText: string, suggestion: string, segments: SuggestionSegment[]): SuggestionOption {
|
|
278
400
|
const furthestMatch = findMatchIndex(suggestion, fullText);
|
|
279
401
|
const text = suggestion.slice(furthestMatch);
|
|
280
402
|
|
|
281
403
|
const option:SuggestionOption = {
|
|
282
404
|
text: text,
|
|
283
405
|
startIndex: furthestMatch,
|
|
406
|
+
suggestionSequence: segments,
|
|
284
407
|
};
|
|
285
408
|
return option
|
|
286
409
|
}
|
|
@@ -1,4 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { Pattern } from "../patterns/Pattern";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A CompositeSuggestion associated with a index to start at when inserting into existing text.
|
|
6
|
+
*/
|
|
7
|
+
export interface SuggestionOption extends CompositeSuggestion {
|
|
3
8
|
startIndex: number;
|
|
4
9
|
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Represents a composed suggestion, derived from potential next tokens defined by the pattern, and configured auto complete options
|
|
14
|
+
* text: represents the full composed text of the suggestion; a concatenated string of relevant suggestion segments.
|
|
15
|
+
*/
|
|
16
|
+
export interface CompositeSuggestion{
|
|
17
|
+
text: string;
|
|
18
|
+
suggestionSequence: SuggestionSegment[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
/** A segment of a parent composite suggestion and a reference to the pattern that this segment token was derived from. */
|
|
23
|
+
export interface SuggestionSegment {
|
|
24
|
+
text: string;
|
|
25
|
+
pattern: Pattern;
|
|
26
|
+
}
|
|
27
|
+
|
|
@@ -5,6 +5,6 @@ import method from "./method";
|
|
|
5
5
|
import name from "./name"
|
|
6
6
|
import { Options } from "../../patterns/Options";
|
|
7
7
|
|
|
8
|
-
const value = new
|
|
8
|
+
const value = new Options("value", [hex, method, unit, number, name]);
|
|
9
9
|
|
|
10
10
|
export default value;
|
|
@@ -112,7 +112,7 @@ export class Expression implements Pattern {
|
|
|
112
112
|
|
|
113
113
|
private _organizePatterns(patterns: Pattern[]) {
|
|
114
114
|
const finalPatterns: Pattern[] = [];
|
|
115
|
-
patterns.forEach((pattern) => {
|
|
115
|
+
patterns.forEach((pattern, index) => {
|
|
116
116
|
|
|
117
117
|
if (this._isAtom(pattern)) {
|
|
118
118
|
const atom = pattern.clone();
|
|
@@ -127,6 +127,7 @@ export class Expression implements Pattern {
|
|
|
127
127
|
|
|
128
128
|
prefix.parent = this;
|
|
129
129
|
|
|
130
|
+
this._precedenceMap[name] = index;
|
|
130
131
|
this._prefixPatterns.push(prefix);
|
|
131
132
|
this._prefixNames.push(name);
|
|
132
133
|
|
|
@@ -136,6 +137,7 @@ export class Expression implements Pattern {
|
|
|
136
137
|
const postfix = this._extractPostfix(pattern);
|
|
137
138
|
postfix.parent = this;
|
|
138
139
|
|
|
140
|
+
this._precedenceMap[name] = index;
|
|
139
141
|
this._postfixPatterns.push(postfix);
|
|
140
142
|
this._postfixNames.push(name);
|
|
141
143
|
|
|
@@ -145,7 +147,7 @@ export class Expression implements Pattern {
|
|
|
145
147
|
const binary = this._extractBinary(pattern);
|
|
146
148
|
binary.parent = this;
|
|
147
149
|
|
|
148
|
-
this._precedenceMap[name] =
|
|
150
|
+
this._precedenceMap[name] = index;
|
|
149
151
|
this._binaryPatterns.push(binary);
|
|
150
152
|
this._binaryNames.push(name);
|
|
151
153
|
|
|
@@ -175,14 +175,38 @@ export class PrecedenceTree {
|
|
|
175
175
|
private _compileAtomNode() {
|
|
176
176
|
let node = this._atomNode;
|
|
177
177
|
|
|
178
|
-
if (this._prefixNode != null && this._atomNode != null) {
|
|
179
|
-
|
|
180
|
-
this.
|
|
181
|
-
|
|
178
|
+
if (this._prefixNode != null && this._postfixNode != null && this._atomNode != null) {
|
|
179
|
+
let firstNode = this._prefixNode;
|
|
180
|
+
let secondNode = this._postfixNode;
|
|
181
|
+
let firstPlaceholder = this._prefixPlaceholder;
|
|
182
|
+
let secondPlaceholder = this._postfixPlaceholder;
|
|
183
|
+
const prefixName = this._prefixNode.name;
|
|
184
|
+
const postfixName = this._postfixNode.name;
|
|
185
|
+
const prefixPrecedence = this._getPrecedence(prefixName);
|
|
186
|
+
const postfixPrecedence = this._getPrecedence(postfixName);
|
|
187
|
+
|
|
188
|
+
// The Precedence is the index of the patterns, so its lower not higher :\
|
|
189
|
+
if (prefixPrecedence > postfixPrecedence) {
|
|
190
|
+
firstNode = this._postfixNode;
|
|
191
|
+
secondNode = this._prefixNode;
|
|
192
|
+
firstPlaceholder = this._postfixPlaceholder;
|
|
193
|
+
secondPlaceholder = this._prefixPlaceholder;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
node = firstNode.findRoot();
|
|
197
|
+
firstPlaceholder.replaceWith(this._atomNode);
|
|
198
|
+
secondPlaceholder.replaceWith(node);
|
|
199
|
+
node = secondNode;
|
|
200
|
+
} else {
|
|
201
|
+
if (this._prefixNode != null && this._atomNode != null) {
|
|
202
|
+
node = this._prefixNode.findRoot();
|
|
203
|
+
this._prefixPlaceholder.replaceWith(this._atomNode);
|
|
204
|
+
}
|
|
182
205
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
206
|
+
if (this._postfixNode != null && node != null) {
|
|
207
|
+
this._postfixPlaceholder.replaceWith(node);
|
|
208
|
+
node = this._postfixNode;
|
|
209
|
+
}
|
|
186
210
|
}
|
|
187
211
|
|
|
188
212
|
this._prefixNode = null;
|
|
@@ -7,15 +7,15 @@ import { Pattern } from "./Pattern";
|
|
|
7
7
|
|
|
8
8
|
describe("Regex", () => {
|
|
9
9
|
test("Empty String", () => {
|
|
10
|
-
expect(() => new Regex("empty", "")).
|
|
10
|
+
expect(() => new Regex("empty", "")).toThrow();
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
test("Starts With ^", () => {
|
|
14
|
-
expect(() => new Regex("carrot", "^")).
|
|
14
|
+
expect(() => new Regex("carrot", "^")).toThrow();
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
test("Ends With $", () => {
|
|
18
|
-
expect(() => new Regex("money", ".$")).
|
|
18
|
+
expect(() => new Regex("money", ".$")).toThrow();
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
test("Successful Parse", () => {
|
package/tsconfig.json
CHANGED