Skip to content

Commit

Permalink
Improve regexp by location parser (check correct group match)
Browse files Browse the repository at this point in the history
  • Loading branch information
dzonatan committed Mar 27, 2017
1 parent 491a030 commit 0db0e27
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 17 deletions.
8 changes: 4 additions & 4 deletions src/providers/angular-html-definition-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ export class AngularHtmlDefinitionProvider implements DefinitionProvider {

const regexps = [
// Interpolation. ex: {{ myProp }}
/{{(.*)}}/g,
/({{)([^}]+)}}/g,

// Input attributes. ex: [(...)]="myProp"
/\[\(?[\w\.\-?]*\)?\]=\"(.*)"/g,
/(\[\(?[\w\.\-?]*\)?\]=")([^"]+)"/g,

// Output attributes. ex: (...)="myMethod(...)"
/\([\w\.]*\)=\"(.*)\"/g,
/(\([\w\.]*\)=")([^"]+)"/g,

// Structural attributes. ex: *ngIf="myProp"
/\*\w+=\"(.*)\"/g
/(\*\w+=")([^"]+)"/g
];
const expressionMatch: string = utils.parseByLocationRegexps(lineText, position.character, regexps);
if (!expressionMatch) return null;
Expand Down
31 changes: 19 additions & 12 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Position } from 'vscode';

/**
* Parse word by regexp and location. Only works with one match group.
* Parse text by regexp and position. WARNING: Uses very specific approach..
* Should always have TWO side by side match groups.
* First group is used to find out second match group position.
* Ex. /(\*\w+=\")(.*)\"/g
* @param text - Text to parse from.
* @param position - Position used in case of multiple occurrences.
* @param regexp - Regexp to match.
Expand All @@ -10,14 +13,20 @@ export function parseByLocationRegexp(text: string, position: number, regexp: Re
let propertyName: string = null;
let match: RegExpMatchArray;
while (match = regexp.exec(text)) {
// This is necessary to avoid infinite loops with zero-width matches
if (match.index === regexp.lastIndex) {
regexp.lastIndex++;
}

// Check if position is in whole regexp match
if (match.index <= position && regexp.lastIndex >= position) {
// Check if position is in concrete first group match
const matchedGroupIndex = match.index + match[0].indexOf(match[1]);
const matchedGroupLastIndex = matchedGroupIndex + match[1].length;
if (matchedGroupIndex <= position && matchedGroupLastIndex >= position)
{
propertyName = match[1];
if (match.index <= position && regexp.lastIndex >= position && match.length === 3) {

// Check position of group match
const matchedGroupIndex = match.index + match[match.length - 2].length;
const matchedGroupLastIndex = matchedGroupIndex + match[match.length - 1].length;
if (matchedGroupIndex <= position && matchedGroupLastIndex >= position) {
propertyName = match[match.length - 1];
break;
}
}
}
Expand All @@ -27,10 +36,8 @@ export function parseByLocationRegexp(text: string, position: number, regexp: Re

export function parseByLocationRegexps(text: string, position: number, regexps: RegExp[]) {
for (let regexp of regexps) {
let result;
if (result = parseByLocationRegexp(text, position, regexp)) {
return result;
}
let result = parseByLocationRegexp(text, position, regexp)
if (result) return result;
}

return null;
Expand Down
2 changes: 1 addition & 1 deletion test/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as assert from 'assert';
suite('Utils Tests', () => {
suite('parseByLocationRegexp', () => {
test('should match group only in given position', () => {
const regexp = /{{ (\w+) }}/g;
const regexp = /({{ )(\w+) }}/g;
const input = '<span>{{ myVar1 }} {{ myVar2 }}</span>';

assert.equal(parseByLocationRegexp(input, 8, regexp), null);
Expand Down

0 comments on commit 0db0e27

Please sign in to comment.