clarity-pattern-parser 4.0.3 → 6.0.0
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/README.md +466 -1
- package/TODO.md +76 -2
- package/dist/ast/Node.d.ts +49 -11
- package/dist/ast/Visitor.d.ts +31 -31
- package/dist/index.browser.js +1513 -1495
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +17 -17
- package/dist/index.esm.js +1480 -1459
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1481 -1463
- package/dist/index.js.map +1 -1
- package/dist/intellisense/AutoComplete.d.ts +28 -0
- package/dist/intellisense/Suggestion.d.ts +11 -0
- package/dist/intellisense/SuggestionOption.d.ts +4 -0
- package/dist/patterns/And.d.ts +37 -24
- package/dist/patterns/Cursor.d.ts +37 -0
- package/dist/patterns/CursorHistory.d.ts +30 -0
- package/dist/patterns/Literal.d.ts +35 -19
- package/dist/patterns/Not.d.ts +29 -11
- package/dist/patterns/Or.d.ts +33 -22
- package/dist/patterns/ParseError.d.ts +6 -8
- package/dist/patterns/ParseResult.d.ts +6 -0
- package/dist/patterns/Pattern.d.ts +20 -26
- package/dist/patterns/Reference.d.ts +34 -12
- package/dist/patterns/Regex.d.ts +41 -21
- package/dist/patterns/Repeat.d.ts +38 -20
- package/dist/patterns/clonePatterns.d.ts +2 -0
- package/dist/patterns/filterOutNull.d.ts +2 -0
- package/dist/patterns/findPattern.d.ts +2 -0
- package/dist/patterns/getNextPattern.d.ts +2 -0
- package/jest.config.js +2 -1
- package/package.json +4 -5
- package/rollup.config.js +1 -1
- package/src/ast/Node.test.ts +364 -0
- package/src/ast/Node.ts +237 -23
- package/src/index.ts +25 -27
- package/src/intellisense/AutoComplete.test.ts +150 -0
- package/src/intellisense/AutoComplete.ts +200 -0
- package/src/intellisense/Suggestion.ts +12 -0
- package/src/intellisense/SuggestionOption.ts +4 -0
- package/src/{tests/cssPatterns → intellisense/css}/cssValue.ts +1 -1
- package/src/{tests/cssPatterns → intellisense/css}/divider.ts +2 -1
- package/src/intellisense/css/hex.ts +6 -0
- package/src/{tests/cssPatterns → intellisense/css}/method.ts +8 -9
- package/src/intellisense/css/name.ts +5 -0
- package/src/{tests/javascriptPatterns → intellisense/css}/number.ts +3 -3
- package/src/intellisense/css/spaces.ts +6 -0
- package/src/intellisense/css/unit.ts +10 -0
- package/src/{tests/cssPatterns → intellisense/css}/value.ts +1 -1
- package/src/{tests/cssPatterns → intellisense/css}/values.ts +1 -1
- package/src/intellisense/javascript/Javascript.test.ts +203 -0
- package/src/intellisense/javascript/arrayLiteral.ts +25 -0
- package/src/intellisense/javascript/deleteStatement.ts +14 -0
- package/src/intellisense/javascript/escapedCharacter.ts +49 -0
- package/src/intellisense/javascript/exponent.ts +24 -0
- package/src/intellisense/javascript/expression.ts +87 -0
- package/src/intellisense/javascript/expressionStatement.ts +29 -0
- package/src/intellisense/javascript/fraction.ts +11 -0
- package/src/intellisense/javascript/infixOperator.ts +36 -0
- package/src/intellisense/javascript/integer.ts +7 -0
- package/src/intellisense/javascript/invocation.ts +28 -0
- package/src/intellisense/javascript/literal.ts +14 -0
- package/src/intellisense/javascript/name.ts +3 -0
- package/src/intellisense/javascript/numberLiteral.ts +10 -0
- package/src/intellisense/javascript/objectLiteral.ts +30 -0
- package/src/intellisense/javascript/optionalSpaces.ts +3 -0
- package/src/intellisense/javascript/parameters.ts +20 -0
- package/src/intellisense/javascript/prefixOperator.ts +13 -0
- package/src/intellisense/javascript/propertyAccess.ts +23 -0
- package/src/intellisense/javascript/stringLiteral.ts +28 -0
- package/src/patterns/And.test.ts +310 -0
- package/src/patterns/And.ts +244 -119
- package/src/patterns/Cursor.test.ts +93 -0
- package/src/patterns/Cursor.ts +133 -0
- package/src/patterns/CursorHistory.test.ts +54 -0
- package/src/patterns/CursorHistory.ts +95 -0
- package/src/patterns/Literal.test.ts +166 -0
- package/src/patterns/Literal.ts +141 -62
- package/src/patterns/Not.test.ts +168 -0
- package/src/patterns/Not.ts +113 -32
- package/src/patterns/Or.test.ts +209 -0
- package/src/patterns/Or.ts +128 -97
- package/src/patterns/ParseError.ts +3 -7
- package/src/patterns/ParseResult.ts +7 -0
- package/src/patterns/Pattern.ts +21 -150
- package/src/patterns/Reference.test.ts +193 -0
- package/src/patterns/Reference.ts +114 -88
- package/src/patterns/Regex.test.ts +133 -0
- package/src/patterns/Regex.ts +117 -60
- package/src/patterns/Repeat.test.ts +218 -0
- package/src/patterns/Repeat.ts +220 -103
- package/src/patterns/clonePatterns.ts +5 -0
- package/src/patterns/filterOutNull.ts +13 -0
- package/src/patterns/findPattern.ts +25 -0
- package/src/Cursor.ts +0 -141
- package/src/CursorHistory.ts +0 -146
- package/src/TextSuggester.ts +0 -317
- package/src/ast/Visitor.ts +0 -271
- package/src/patterns/LookAhead.ts +0 -32
- package/src/patterns/Recursive.ts +0 -92
- package/src/tests/And.test.ts +0 -180
- package/src/tests/ComplexExamples.test.ts +0 -86
- package/src/tests/CssPatterns.test.ts +0 -90
- package/src/tests/CursorHistory.test.ts +0 -107
- package/src/tests/Cusor.test.ts +0 -174
- package/src/tests/HtmlPatterns.test.ts +0 -34
- package/src/tests/Literal.test.ts +0 -79
- package/src/tests/LookAhead.test.ts +0 -44
- package/src/tests/Not.test.ts +0 -51
- package/src/tests/Or.test.ts +0 -113
- package/src/tests/Pattern.test.ts +0 -290
- package/src/tests/Recursive.test.ts +0 -64
- package/src/tests/Reference.test.ts +0 -16
- package/src/tests/Repeat.test.ts +0 -75
- package/src/tests/SpeedTest.test.ts +0 -31
- package/src/tests/TextSuggester.test.ts +0 -297
- package/src/tests/Visitor.test.ts +0 -331
- package/src/tests/cssPatterns/hex.ts +0 -5
- package/src/tests/cssPatterns/name.ts +0 -5
- package/src/tests/cssPatterns/number.ts +0 -8
- package/src/tests/cssPatterns/spaces.ts +0 -5
- package/src/tests/cssPatterns/unit.ts +0 -8
- package/src/tests/htmlPatterns/element.ts +0 -49
- package/src/tests/javascriptPatterns/boolean.ts +0 -10
- package/src/tests/javascriptPatterns/json.ts +0 -67
- package/src/tests/javascriptPatterns/name.ts +0 -5
- package/src/tests/javascriptPatterns/objectLiteral.ts +0 -40
- package/src/tests/javascriptPatterns/string.ts +0 -84
- package/src/tests/javascriptPatterns/unit.ts +0 -8
- package/src/tests/javascriptPatterns/whitespace.ts +0 -44
- package/src/tests/naturalLanguage/filter.ts +0 -37
- package/src/tests/patterns/sentence.ts +0 -37
- /package/src/{tests/cssPatterns → intellisense/css}/optionalSpaces.ts +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { And } from "../patterns/And";
|
|
2
|
+
import { findPattern } from "../patterns/findPattern";
|
|
3
|
+
import { Literal } from "../patterns/Literal";
|
|
4
|
+
import { Or } from "../patterns/Or";
|
|
5
|
+
import { Regex } from "../patterns/Regex";
|
|
6
|
+
import { Repeat } from "../patterns/Repeat";
|
|
7
|
+
import { AutoComplete, AutoCompleteOptions } from "./AutoComplete";
|
|
8
|
+
|
|
9
|
+
describe("AutoComplete", () => {
|
|
10
|
+
test("No Text", () => {
|
|
11
|
+
const name = new Literal("name", "Name");
|
|
12
|
+
const autoComplete = new AutoComplete(name);
|
|
13
|
+
let result = autoComplete.suggest("");
|
|
14
|
+
|
|
15
|
+
expect(result.options[0].text).toBe("Name");
|
|
16
|
+
expect(result.options[0].startIndex).toBe(0);
|
|
17
|
+
expect(result.nextPatterns[0]).toBe(name);
|
|
18
|
+
expect(result.isComplete).toBeFalsy();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("Full Pattern Match", () => {
|
|
22
|
+
const john = new Literal("john", "John");
|
|
23
|
+
const space = new Literal("space", " ");
|
|
24
|
+
const doe = new Literal("doe", "Doe");
|
|
25
|
+
const smith = new Literal("smith", "Smith");
|
|
26
|
+
const name = new And("name", [john, space, new Or("last-name", [smith, doe])]);
|
|
27
|
+
|
|
28
|
+
const autoComplete = new AutoComplete(name);
|
|
29
|
+
const result = autoComplete.suggest("John Doe");
|
|
30
|
+
|
|
31
|
+
expect(result.ast?.value).toBe("John Doe");
|
|
32
|
+
expect(result.options.length).toBe(0);
|
|
33
|
+
expect(result.nextPatterns.length).toBe(0);
|
|
34
|
+
expect(result.isComplete).toBeTruthy();
|
|
35
|
+
expect(result.cursor).not.toBeNull();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("More Than One Option", () => {
|
|
39
|
+
const john = new Literal("john", "John");
|
|
40
|
+
const space = new Literal("space", " ");
|
|
41
|
+
const doe = new Literal("doe", "Doe");
|
|
42
|
+
const smith = new Literal("smith", "Smith");
|
|
43
|
+
const name = new And("name", [john, space, new Or("last-name", [smith, doe])]);
|
|
44
|
+
|
|
45
|
+
const autoComplete = new AutoComplete(name);
|
|
46
|
+
const result = autoComplete.suggest("John ");
|
|
47
|
+
|
|
48
|
+
expect(result.ast).toBeNull();
|
|
49
|
+
expect(result.options.length).toBe(2);
|
|
50
|
+
expect(result.nextPatterns.length).toBe(1);
|
|
51
|
+
expect(result.nextPatterns[0].type).toBe("or");
|
|
52
|
+
expect(result.nextPatterns[0].name).toBe("last-name");
|
|
53
|
+
expect(result.isComplete).toBeFalsy();
|
|
54
|
+
expect(result.cursor).not.toBeNull();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("Full Pattern Match With Root Repeat", () => {
|
|
58
|
+
const john = new Literal("john", "John");
|
|
59
|
+
const space = new Literal("space", " ");
|
|
60
|
+
const doe = new Literal("doe", "Doe");
|
|
61
|
+
const smith = new Literal("smith", "Smith");
|
|
62
|
+
const name = new And("name", [john, space, new Or("last-name", [smith, doe])]);
|
|
63
|
+
const divider = new Regex("divider", "\\s+,\\s+");
|
|
64
|
+
|
|
65
|
+
divider.setTokens([", "])
|
|
66
|
+
|
|
67
|
+
const autoComplete = new AutoComplete(new Repeat("last-names", name, divider));
|
|
68
|
+
const result = autoComplete.suggest("John Doe");
|
|
69
|
+
|
|
70
|
+
expect(result.ast?.value).toBe("John Doe");
|
|
71
|
+
expect(result.options.length).toBe(1);
|
|
72
|
+
expect(result.nextPatterns.length).toBe(result.options.length);
|
|
73
|
+
expect(result.isComplete).toBeTruthy();
|
|
74
|
+
expect(result.cursor).not.toBeNull();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("Partial", () => {
|
|
78
|
+
const name = new Literal("name", "Name");
|
|
79
|
+
const autoComplete = new AutoComplete(name);
|
|
80
|
+
let result = autoComplete.suggest("Na");
|
|
81
|
+
|
|
82
|
+
expect(result.options[0].text).toBe("me");
|
|
83
|
+
expect(result.options[0].startIndex).toBe(2);
|
|
84
|
+
expect(result.nextPatterns[0]).toBe(name);
|
|
85
|
+
expect(result.isComplete).toBeFalsy();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("Partial Match With Bad Characters", () => {
|
|
89
|
+
const name = new Literal("name", "Name");
|
|
90
|
+
const autoComplete = new AutoComplete(name);
|
|
91
|
+
let result = autoComplete.suggest("Ni");
|
|
92
|
+
|
|
93
|
+
expect(result.options[0].text).toBe("ame");
|
|
94
|
+
expect(result.options[0].startIndex).toBe(1);
|
|
95
|
+
//expect(result.nextPattern).toBe(name);
|
|
96
|
+
expect(result.isComplete).toBeFalsy();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("Complete", () => {
|
|
100
|
+
const name = new Literal("name", "Name");
|
|
101
|
+
const autoComplete = new AutoComplete(name);
|
|
102
|
+
let result = autoComplete.suggest("Name");
|
|
103
|
+
|
|
104
|
+
expect(result.options.length).toBe(0);
|
|
105
|
+
expect(result.nextPatterns.length).toBe(0);
|
|
106
|
+
expect(result.isComplete).toBeTruthy();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("Options", ()=>{
|
|
110
|
+
const autoCompleteOptions: AutoCompleteOptions = {
|
|
111
|
+
greedyPatternNames: ["space"],
|
|
112
|
+
customTokens: {
|
|
113
|
+
"last-name": ["Sparrow"]
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const jack = new Literal("jack", "Jack");
|
|
118
|
+
const john = new Literal("john", "John");
|
|
119
|
+
const space = new Literal("space", " ");
|
|
120
|
+
const doe = new Literal("doe", "Doe");
|
|
121
|
+
const smith = new Literal("smith", "Smith");
|
|
122
|
+
const firstName = new Or("first-name", [jack, john]);
|
|
123
|
+
const lastName = new Or("last-name", [doe, smith]);
|
|
124
|
+
const fullName = new And("full-name", [firstName, space, lastName]);
|
|
125
|
+
|
|
126
|
+
const text = "John";
|
|
127
|
+
const autoComplete = new AutoComplete(fullName, autoCompleteOptions);
|
|
128
|
+
const { options, ast, nextPatterns } = autoComplete.suggest(text);
|
|
129
|
+
const expectedOptions = [
|
|
130
|
+
{text: " Doe", startIndex: 4},
|
|
131
|
+
{text: " Smith", startIndex: 4},
|
|
132
|
+
{text: " Sparrow", startIndex: 4},
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
const results = expectedOptions.map(o=>text.slice(0, o.startIndex) + o.text);
|
|
136
|
+
const expectedResults = [
|
|
137
|
+
"John Doe",
|
|
138
|
+
"John Smith",
|
|
139
|
+
"John Sparrow",
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
expect(options).toEqual(expectedOptions);
|
|
143
|
+
expect(ast).toBeNull();
|
|
144
|
+
expect(nextPatterns.length).toBe(1);
|
|
145
|
+
expect(nextPatterns[0].name).toBe("space");
|
|
146
|
+
expect(results).toEqual(expectedResults)
|
|
147
|
+
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
});
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { Cursor } from "../patterns/Cursor";
|
|
2
|
+
import { Pattern } from "../patterns/Pattern";
|
|
3
|
+
import { Suggestion } from "./Suggestion";
|
|
4
|
+
import { SuggestionOption } from "./SuggestionOption";
|
|
5
|
+
|
|
6
|
+
export interface AutoCompleteOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Allows for certain patterns to combine their tokens with the next tokens.
|
|
9
|
+
* Be very careful, this can explode to infinity pretty quick. Usually useful
|
|
10
|
+
* for dividers and spaces.
|
|
11
|
+
*/
|
|
12
|
+
greedyPatternNames: string[];
|
|
13
|
+
/**
|
|
14
|
+
* Allows for custom suggestions for patterns. The key is the name of the pattern
|
|
15
|
+
* and the string array are the tokens suggested for that pattern.
|
|
16
|
+
*/
|
|
17
|
+
customTokens: Record<string, string[]>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const defaultOptions = { greedyPatternNames: [], customTokens: {} };
|
|
21
|
+
|
|
22
|
+
export class AutoComplete {
|
|
23
|
+
private _pattern: Pattern;
|
|
24
|
+
private _options: AutoCompleteOptions;
|
|
25
|
+
private _cursor!: Cursor;
|
|
26
|
+
private _text: string;
|
|
27
|
+
|
|
28
|
+
constructor(pattern: Pattern, options: AutoCompleteOptions = defaultOptions) {
|
|
29
|
+
this._pattern = pattern;
|
|
30
|
+
this._options = options;
|
|
31
|
+
this._text = "";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
suggest(text: string): Suggestion {
|
|
35
|
+
if (text.length === 0) {
|
|
36
|
+
return {
|
|
37
|
+
isComplete: false,
|
|
38
|
+
options: this.createSuggestionsFromRoot(),
|
|
39
|
+
nextPatterns: [this._pattern],
|
|
40
|
+
cursor: null,
|
|
41
|
+
ast: null
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this._text = text;
|
|
46
|
+
this._cursor = new Cursor(text);
|
|
47
|
+
const ast = this._pattern.parse(this._cursor);
|
|
48
|
+
|
|
49
|
+
const leafPattern = this._cursor.leafMatch.pattern;
|
|
50
|
+
const isComplete = ast?.value === text;
|
|
51
|
+
const options = this.createSuggestionsFromTokens();
|
|
52
|
+
|
|
53
|
+
let nextPatterns = [this._pattern];
|
|
54
|
+
|
|
55
|
+
if (leafPattern != null) {
|
|
56
|
+
nextPatterns = leafPattern.getNextPatterns();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
isComplete: isComplete,
|
|
61
|
+
options: options,
|
|
62
|
+
nextPatterns,
|
|
63
|
+
cursor: this._cursor,
|
|
64
|
+
ast,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private createSuggestionsFromRoot(): SuggestionOption[] {
|
|
69
|
+
const suggestions: SuggestionOption[] = [];
|
|
70
|
+
const tokens = this._pattern.getTokens();
|
|
71
|
+
|
|
72
|
+
for (const token of tokens) {
|
|
73
|
+
suggestions.push(this.createSuggestion("", token));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return suggestions;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private createSuggestionsFromTokens(): SuggestionOption[] {
|
|
80
|
+
const leafMatch = this._cursor.leafMatch;
|
|
81
|
+
|
|
82
|
+
if (!leafMatch.pattern) {
|
|
83
|
+
return this.createSuggestions(-1, this._getTokensForPattern(this._pattern));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const leafPattern = leafMatch.pattern;
|
|
87
|
+
const leafNode = leafMatch.node;
|
|
88
|
+
const parent = leafMatch.pattern.parent;
|
|
89
|
+
|
|
90
|
+
if (parent !== null && leafMatch.node != null) {
|
|
91
|
+
const patterns = leafPattern.getNextPatterns();
|
|
92
|
+
|
|
93
|
+
const tokens = patterns.reduce((acc: string[], pattern) => {
|
|
94
|
+
acc.push(...this._getTokensForPattern(pattern));
|
|
95
|
+
return acc;
|
|
96
|
+
}, []);
|
|
97
|
+
|
|
98
|
+
return this.createSuggestions(leafMatch.node.lastIndex, tokens);
|
|
99
|
+
} else {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private _getTokensForPattern(pattern: Pattern) {
|
|
105
|
+
if (this._options.greedyPatternNames.includes(pattern.name)) {
|
|
106
|
+
const greedyTokens = pattern.getTokens();
|
|
107
|
+
const nextPatterns = pattern.getNextPatterns();
|
|
108
|
+
const tokens: string[] = [];
|
|
109
|
+
|
|
110
|
+
const nextPatternTokens = nextPatterns.reduce((acc: string[], pattern)=>{
|
|
111
|
+
acc.push(...this._getTokensForPattern(pattern));
|
|
112
|
+
return acc;
|
|
113
|
+
}, []);
|
|
114
|
+
|
|
115
|
+
for (let token of greedyTokens){
|
|
116
|
+
for (let nextPatternToken of nextPatternTokens){
|
|
117
|
+
tokens.push(token + nextPatternToken);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return tokens;
|
|
122
|
+
} else {
|
|
123
|
+
const tokens = pattern.getTokens();
|
|
124
|
+
const customTokens = this._options.customTokens[pattern.name] || [];
|
|
125
|
+
tokens.push(...customTokens);
|
|
126
|
+
return tokens;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private createSuggestions(lastIndex: number, tokens: string[]): SuggestionOption[] {
|
|
131
|
+
let substring = lastIndex === -1 ? "" : this._cursor.getChars(0, lastIndex);
|
|
132
|
+
const suggestionStrings: string[] = [];
|
|
133
|
+
const options: SuggestionOption[] = [];
|
|
134
|
+
|
|
135
|
+
for (const token of tokens) {
|
|
136
|
+
const suggestion = substring + token;
|
|
137
|
+
const startsWith = suggestion.startsWith(substring);
|
|
138
|
+
const alreadyExist = suggestionStrings.includes(suggestion);
|
|
139
|
+
const isSameAsText = suggestion === this._text;
|
|
140
|
+
|
|
141
|
+
if (startsWith && !alreadyExist && !isSameAsText) {
|
|
142
|
+
suggestionStrings.push(suggestion);
|
|
143
|
+
options.push(this.createSuggestion(this._cursor.text, suggestion));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const reducedOptions = getFurthestOptions(options);
|
|
148
|
+
reducedOptions.sort((a, b) => a.text.localeCompare(b.text));
|
|
149
|
+
|
|
150
|
+
return reducedOptions;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private createSuggestion(fullText: string, suggestion: string): SuggestionOption {
|
|
154
|
+
const furthestMatch = findMatchIndex(suggestion, fullText);
|
|
155
|
+
const text = suggestion.slice(furthestMatch);
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
text: text,
|
|
159
|
+
startIndex: furthestMatch,
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function findMatchIndex(str1: string, str2: string): number {
|
|
165
|
+
let matchCount = 0;
|
|
166
|
+
let minLength = str1.length;
|
|
167
|
+
|
|
168
|
+
if (str2.length < minLength) {
|
|
169
|
+
minLength = str2.length;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for (let i = 0; i < minLength; i++) {
|
|
173
|
+
if (str1[i] === str2[i]) {
|
|
174
|
+
matchCount++;
|
|
175
|
+
} else {
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return matchCount;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function getFurthestOptions(options: SuggestionOption[]): SuggestionOption[] {
|
|
184
|
+
let furthestOptions: SuggestionOption[] = [];
|
|
185
|
+
let furthestIndex = -1;
|
|
186
|
+
|
|
187
|
+
for (const option of options) {
|
|
188
|
+
if (option.startIndex > furthestIndex) {
|
|
189
|
+
furthestIndex = option.startIndex;
|
|
190
|
+
furthestOptions = [];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (option.startIndex === furthestIndex) {
|
|
194
|
+
furthestOptions.push(option);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return furthestOptions;
|
|
199
|
+
}
|
|
200
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { Cursor } from "../patterns/Cursor";
|
|
3
|
+
import { Pattern } from "../patterns/Pattern";
|
|
4
|
+
import { SuggestionOption } from "./SuggestionOption";
|
|
5
|
+
|
|
6
|
+
export interface Suggestion {
|
|
7
|
+
isComplete: boolean;
|
|
8
|
+
options: SuggestionOption[];
|
|
9
|
+
nextPatterns: Pattern[];
|
|
10
|
+
cursor: Cursor | null;
|
|
11
|
+
ast: Node | null;
|
|
12
|
+
}
|
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Recursive,
|
|
6
|
-
} from "../../index";
|
|
7
|
-
|
|
1
|
+
import { Reference } from "../../patterns/Reference";
|
|
2
|
+
import { Literal } from "../../patterns/Literal";
|
|
3
|
+
import { Repeat } from "../../patterns/Repeat";
|
|
4
|
+
import { And } from "../../patterns/And";
|
|
8
5
|
import name from "./name";
|
|
9
6
|
import optionalSpaces from "./optionalSpaces";
|
|
10
7
|
import divider from "./divider";
|
|
11
8
|
|
|
12
9
|
const openParen = new Literal("open-paren", "(");
|
|
13
10
|
const closeParen = new Literal("close-paren", ")");
|
|
14
|
-
const values = new
|
|
11
|
+
const values = new Reference("values");
|
|
15
12
|
const args = new Repeat("arguments", values, divider, true);
|
|
13
|
+
const methodName = name.clone("method-name");
|
|
14
|
+
methodName.setTokens(["rgba", "radial-gradient", "linear-gradient"]);
|
|
16
15
|
|
|
17
16
|
const method = new And("method", [
|
|
18
|
-
|
|
17
|
+
methodName,
|
|
19
18
|
openParen,
|
|
20
19
|
optionalSpaces,
|
|
21
20
|
args,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { And } from "../../patterns/And";
|
|
2
|
+
import { Regex } from "../../patterns/Regex";
|
|
3
|
+
import number from "./number";
|
|
4
|
+
|
|
5
|
+
const unitType = new Regex("unit-type", "[a-zA-Z%]+");
|
|
6
|
+
unitType.setTokens(["px", "%", "em", "rem", "vh", "vw"]);
|
|
7
|
+
|
|
8
|
+
const unit = new And("unit", [number, unitType]);
|
|
9
|
+
|
|
10
|
+
export default unit;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Or } from "../../index";
|
|
2
1
|
import unit from "./unit";
|
|
3
2
|
import hex from "./hex";
|
|
4
3
|
import number from "./number";
|
|
5
4
|
import method from "./method";
|
|
6
5
|
import name from "./name"
|
|
6
|
+
import { Or } from "../../patterns/Or";
|
|
7
7
|
|
|
8
8
|
const value = new Or("value", [hex, method, unit, number, name]);
|
|
9
9
|
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { escapedCharacter } from "./escapedCharacter";
|
|
2
|
+
import { exponent } from "./exponent";
|
|
3
|
+
import { expression } from "./expression";
|
|
4
|
+
import { expressionStatement } from "./expressionStatement";
|
|
5
|
+
import { infixOperator } from "./infixOperator";
|
|
6
|
+
import { integer } from "./integer";
|
|
7
|
+
import { name } from "./name";
|
|
8
|
+
import { parameters } from "./parameters";
|
|
9
|
+
import { prefixOperator } from "./prefixOperator";
|
|
10
|
+
|
|
11
|
+
describe("Ecmascript 3", () => {
|
|
12
|
+
test("Escaped Character", () => {
|
|
13
|
+
let result = escapedCharacter.exec(`\\"`);
|
|
14
|
+
expect(result.ast?.value).toBe(`\\"`)
|
|
15
|
+
|
|
16
|
+
result = escapedCharacter.exec(`\\'`)
|
|
17
|
+
expect(result.ast?.value).toBe(`\\'`)
|
|
18
|
+
|
|
19
|
+
result = escapedCharacter.exec(`\\\\`)
|
|
20
|
+
expect(result.ast?.value).toBe(`\\\\`)
|
|
21
|
+
|
|
22
|
+
result = escapedCharacter.exec(`\\/`)
|
|
23
|
+
expect(result.ast?.value).toBe(`\\/`)
|
|
24
|
+
|
|
25
|
+
result = escapedCharacter.exec(`\\f`)
|
|
26
|
+
expect(result.ast?.value).toBe(`\\f`)
|
|
27
|
+
|
|
28
|
+
result = escapedCharacter.exec(`\\t`)
|
|
29
|
+
expect(result.ast?.value).toBe(`\\t`)
|
|
30
|
+
|
|
31
|
+
result = escapedCharacter.exec(`\\u00E9`)
|
|
32
|
+
expect(result.ast?.value).toBe(`\\u00E9`)
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("Exponent", () => {
|
|
36
|
+
let result = exponent.exec("e+1");
|
|
37
|
+
expect(result.ast?.value).toBe("e+1")
|
|
38
|
+
|
|
39
|
+
result = exponent.exec("e-1");
|
|
40
|
+
expect(result.ast?.value).toBe("e-1")
|
|
41
|
+
|
|
42
|
+
result = exponent.exec("E+1");
|
|
43
|
+
expect(result.ast?.value).toBe("E+1")
|
|
44
|
+
|
|
45
|
+
result = exponent.exec("E-1");
|
|
46
|
+
expect(result.ast?.value).toBe("E-1")
|
|
47
|
+
|
|
48
|
+
result = exponent.exec("e+11");
|
|
49
|
+
expect(result.ast?.value).toBe("e+11")
|
|
50
|
+
|
|
51
|
+
result = exponent.exec("11");
|
|
52
|
+
expect(result.ast).toBeNull()
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("Integer", () => {
|
|
56
|
+
let result = integer.exec("0");
|
|
57
|
+
expect(result.ast?.value).toBe("0");
|
|
58
|
+
|
|
59
|
+
result = integer.exec("1");
|
|
60
|
+
expect(result.ast?.value).toBe("1");
|
|
61
|
+
|
|
62
|
+
result = integer.exec("100");
|
|
63
|
+
expect(result.ast?.value).toBe("100");
|
|
64
|
+
|
|
65
|
+
result = integer.exec(".1");
|
|
66
|
+
expect(result.ast).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("Infix Operator", () => {
|
|
70
|
+
let result = infixOperator.exec("*");
|
|
71
|
+
expect(result.ast?.value).toBe("*");
|
|
72
|
+
|
|
73
|
+
result = infixOperator.exec("/");
|
|
74
|
+
expect(result.ast?.value).toBe("/");
|
|
75
|
+
|
|
76
|
+
result = infixOperator.exec("%");
|
|
77
|
+
expect(result.ast?.value).toBe("%");
|
|
78
|
+
|
|
79
|
+
result = infixOperator.exec("+");
|
|
80
|
+
expect(result.ast?.value).toBe("+");
|
|
81
|
+
|
|
82
|
+
result = infixOperator.exec("-");
|
|
83
|
+
expect(result.ast?.value).toBe("-");
|
|
84
|
+
|
|
85
|
+
result = infixOperator.exec(">=");
|
|
86
|
+
expect(result.ast?.value).toBe(">=");
|
|
87
|
+
|
|
88
|
+
result = infixOperator.exec("<=");
|
|
89
|
+
expect(result.ast?.value).toBe("<=");
|
|
90
|
+
|
|
91
|
+
result = infixOperator.exec(">");
|
|
92
|
+
expect(result.ast?.value).toBe(">");
|
|
93
|
+
|
|
94
|
+
result = infixOperator.exec("<");
|
|
95
|
+
expect(result.ast?.value).toBe("<");
|
|
96
|
+
|
|
97
|
+
result = infixOperator.exec("===");
|
|
98
|
+
expect(result.ast?.value).toBe("===");
|
|
99
|
+
|
|
100
|
+
result = infixOperator.exec("!==");
|
|
101
|
+
expect(result.ast?.value).toBe("!==");
|
|
102
|
+
|
|
103
|
+
result = infixOperator.exec("||");
|
|
104
|
+
expect(result.ast?.value).toBe("||");
|
|
105
|
+
|
|
106
|
+
result = infixOperator.exec("&&");
|
|
107
|
+
expect(result.ast?.value).toBe("&&");
|
|
108
|
+
|
|
109
|
+
result = infixOperator.exec("bad");
|
|
110
|
+
expect(result.ast).toBeNull();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
test("Name", () => {
|
|
115
|
+
let result = name.exec("p_0");
|
|
116
|
+
expect(result.ast?.value).toBe("p_0");
|
|
117
|
+
|
|
118
|
+
result = name.exec("_0");
|
|
119
|
+
expect(result.ast?.value).toBe("_0");
|
|
120
|
+
|
|
121
|
+
result = name.exec("0");
|
|
122
|
+
expect(result.ast).toBeNull();
|
|
123
|
+
|
|
124
|
+
result = name.exec("_");
|
|
125
|
+
expect(result.ast?.value).toBe("_");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("Parameters", () => {
|
|
129
|
+
let result = parameters.exec("(param1)");
|
|
130
|
+
expect(result.ast?.value).toBe("(param1)");
|
|
131
|
+
|
|
132
|
+
result = parameters.exec("(param1, param2)");
|
|
133
|
+
expect(result.ast?.value).toBe("(param1, param2)");
|
|
134
|
+
|
|
135
|
+
result = parameters.exec("(param1, param2,)");
|
|
136
|
+
expect(result.ast).toBeNull();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("Prefix Operator", () => {
|
|
140
|
+
let result = prefixOperator.exec("typeof ");
|
|
141
|
+
expect(result.ast?.value).toBe("typeof ");
|
|
142
|
+
|
|
143
|
+
result = prefixOperator.exec("+");
|
|
144
|
+
expect(result.ast?.value).toBe("+");
|
|
145
|
+
|
|
146
|
+
result = prefixOperator.exec("-");
|
|
147
|
+
expect(result.ast?.value).toBe("-");
|
|
148
|
+
|
|
149
|
+
result = prefixOperator.exec("!");
|
|
150
|
+
expect(result.ast?.value).toBe("!");
|
|
151
|
+
|
|
152
|
+
result = prefixOperator.exec("a");
|
|
153
|
+
expect(result.ast).toBeNull();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("Object Literal", () => {
|
|
157
|
+
let result = expression.exec(`{}`)
|
|
158
|
+
expect(result.ast?.value).toBe("{}");
|
|
159
|
+
|
|
160
|
+
result = expression.exec(`{prop:{}}`)
|
|
161
|
+
expect(result.ast?.value).toBe("{prop:{}}");
|
|
162
|
+
|
|
163
|
+
result = expression.exec(`{prop:"value"}`)
|
|
164
|
+
expect(result.ast?.value).toBe(`{prop:"value"}`);
|
|
165
|
+
|
|
166
|
+
result = expression.exec(`{prop:0.9}`)
|
|
167
|
+
expect(result.ast?.value).toBe(`{prop:0.9}`);
|
|
168
|
+
|
|
169
|
+
result = expression.exec(`{prop:1}`)
|
|
170
|
+
expect(result.ast?.value).toBe(`{prop:1}`);
|
|
171
|
+
|
|
172
|
+
result = expression.exec(`{"prop":1}`)
|
|
173
|
+
expect(result.ast?.value).toBe(`{"prop":1}`);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("Array Literal", () => {
|
|
177
|
+
let result = expression.exec("[]")
|
|
178
|
+
expect(result.ast?.value).toBe("[]");
|
|
179
|
+
|
|
180
|
+
result = expression.exec("[{}, 9, 0.9e-10, [1, 2]]")
|
|
181
|
+
expect(result.ast?.value).toBe("[{}, 9, 0.9e-10, [1, 2]]");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("Expression Statement", () => {
|
|
185
|
+
let result = expressionStatement.exec(`name = "John"`);
|
|
186
|
+
expect(result.ast?.value).toBe(`name = "John"`);
|
|
187
|
+
|
|
188
|
+
result = expressionStatement.exec(`name = othername = "John"`)
|
|
189
|
+
expect(result.ast?.value).toBe(`name = othername = "John"`);
|
|
190
|
+
|
|
191
|
+
result = expressionStatement.exec(`name = othername.prop = "John"`)
|
|
192
|
+
expect(result.ast?.value).toBe(`name = othername.prop = "John"`);
|
|
193
|
+
|
|
194
|
+
result = expressionStatement.exec(`name = othername.prop += 2`)
|
|
195
|
+
expect(result.ast?.value).toBe(`name = othername.prop += 2`);
|
|
196
|
+
|
|
197
|
+
result = expressionStatement.exec(`name.prop().method(blah) = blah.prop()`)
|
|
198
|
+
expect(result.ast?.value).toBe(`name.prop().method(blah) = blah.prop()`);
|
|
199
|
+
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
});
|