Fixes #12: Handle differently loop case 1, improve tests and test runner

This commit is contained in:
Alex Dima 2016-05-23 15:14:28 +02:00
Родитель b14ff75250
Коммит 8b9f58ff2c
8 изменённых файлов: 660 добавлений и 201 удалений

Просмотреть файл

@ -80,8 +80,10 @@ gulp.task('watch', ['bundle'], function () {
gulp.task('test', function () {
var tests = require('./release/tests/tests');
tests.runDescriptiveTests(path.join(__dirname, './test-cases/first-mate/tests.json'));
tests.runDescriptiveTests(path.join(__dirname, './test-cases/suite1/tests.json'));
tests.runMatcherTests(path.join(__dirname, './test-cases/matcher/testData.json'));
tests.runTests([
path.join(__dirname, './test-cases/first-mate/tests.json'),
path.join(__dirname, './test-cases/suite1/tests.json')
], [
path.join(__dirname, './test-cases/matcher/testData.json')
]);
});

Просмотреть файл

@ -1312,10 +1312,13 @@ function _tokenizeString(grammar, lineText, isFirstLine, linePos, stack, lineTok
handleCaptures(grammar, lineText, isFirstLine, stack, lineTokens, poppedRule.endCaptures, captureIndices);
lineTokens.produce(stack, captureIndices[0].end);
// pop
stack.pop();
var popped = stack.pop();
if (!hasAdvanced && stackElement.enterPos === linePos) {
// Grammar pushed & popped a rule without advancing
console.error('Grammar is in an endless loop - case 1');
console.error('[1] - Grammar is in an endless loop - Grammar pushed & popped a rule without advancing');
// See https://github.com/Microsoft/vscode-textmate/issues/12
// Let's assume this was a mistake by the grammar author and the intent was to continue in this state
stack.push(popped);
lineTokens.produce(stack, lineLength);
linePos = lineLength;
return false;
@ -1343,7 +1346,7 @@ function _tokenizeString(grammar, lineText, isFirstLine, linePos, stack, lineTok
}
if (!hasAdvanced && stackElement.ruleId === stack[stack.length - 1].ruleId) {
// Grammar pushed the same rule without advancing
console.error('Grammar is in an endless loop - case 2');
console.error('[2] - Grammar is in an endless loop - Grammar pushed the same rule without advancing');
stack.pop();
lineTokens.produce(stack, lineLength);
linePos = lineLength;
@ -1361,7 +1364,7 @@ function _tokenizeString(grammar, lineText, isFirstLine, linePos, stack, lineTok
}
if (!hasAdvanced && stackElement.ruleId === stack[stack.length - 1].ruleId) {
// Grammar pushed the same rule without advancing
console.error('Grammar is in an endless loop - case 2');
console.error('[3] - Grammar is in an endless loop - Grammar pushed the same rule without advancing');
stack.pop();
lineTokens.produce(stack, lineLength);
linePos = lineLength;
@ -1376,7 +1379,7 @@ function _tokenizeString(grammar, lineText, isFirstLine, linePos, stack, lineTok
stack.pop();
if (!hasAdvanced) {
// Grammar is not advancing, nor is it pushing/popping
console.error('Grammar is in an endless loop - case 3');
console.error('[4] - Grammar is in an endless loop - Grammar is not advancing, nor is it pushing/popping');
if (stack.length > 1) {
stack.pop();
}

Просмотреть файл

@ -6,67 +6,136 @@ var fs = require('fs');
var path = require('path');
var main_1 = require('../main');
require('colors');
var errCnt = 0;
function runDescriptiveTests(testLocation) {
var tests = JSON.parse(fs.readFileSync(testLocation).toString());
errCnt = 0;
tests.forEach(function (test, index) {
var desc = test.desc;
var noAsserts = (test.feature === 'endless-loop');
console.log(index + ' - RUNNING ' + desc);
var locator = {
getFilePath: function (scopeName) { return null; },
getInjections: function (scopeName) {
if (scopeName === test.grammarScopeName) {
return test.grammarInjections;
}
return void 0;
var TestResult;
(function (TestResult) {
TestResult[TestResult["Pending"] = 0] = "Pending";
TestResult[TestResult["Success"] = 1] = "Success";
TestResult[TestResult["Failed"] = 2] = "Failed";
})(TestResult || (TestResult = {}));
var Test = (function () {
function Test(testName, runner) {
this.testName = testName;
this._runner = runner;
this.result = TestResult.Pending;
this.failReason = null;
}
Test.prototype.run = function () {
var ctx = new TestContext(this);
try {
this.result = TestResult.Success;
this._runner(ctx);
}
catch (err) {
this.result = TestResult.Failed;
this.failReason = err;
}
};
Test.prototype.fail = function (message, actual, expected) {
this.result = TestResult.Failed;
var reason = [
message
];
if (actual) {
'ACTUAL: ' + reason.push(JSON.stringify(actual, null, '\t'));
}
if (expected) {
'EXPECTED: ' + reason.push(JSON.stringify(expected, null, '\t'));
}
this.failReason = reason.join('\n');
};
return Test;
}());
var TestContext = (function () {
function TestContext(test) {
this._test = test;
}
TestContext.prototype.fail = function (message, actual, expected) {
this._test.fail(message, actual, expected);
};
return TestContext;
}());
var TestManager = (function () {
function TestManager() {
this._tests = [];
}
TestManager.prototype.registerTest = function (testName, runner) {
this._tests.push(new Test(testName, runner));
};
TestManager.prototype.runTests = function () {
var len = this._tests.length;
for (var i = 0; i < len; i++) {
var test = this._tests[i];
var progress = (i + 1) + '/' + len;
console.log(progress.yellow, ': ' + test.testName);
test.run();
if (test.result === TestResult.Failed) {
console.log('FAILED'.red);
console.log(test.failReason);
}
};
var registry = new main_1.Registry(locator);
var grammar = null;
test.grammars.forEach(function (grammarPath) {
var tmpGrammar = registry.loadGrammarFromPathSync(path.join(path.dirname(testLocation), grammarPath));
if (test.grammarPath === grammarPath) {
grammar = tmpGrammar;
}
var passed = this._tests.filter(function (t) { return t.result === TestResult.Success; });
if (passed.length === len) {
console.log((passed.length + '/' + len + ' PASSED.').green);
}
else {
console.log((passed.length + '/' + len + ' PASSED.').red);
}
};
return TestManager;
}());
function runTests(tokenizationTestPaths, matcherTestPaths) {
var manager = new TestManager();
tokenizationTestPaths.forEach(function (path) {
generateTokenizationTests(manager, path);
});
matcherTestPaths.forEach(function (path) {
generateMatcherTests(manager, path);
});
manager.runTests();
}
exports.runTests = runTests;
function generateTokenizationTests(manager, testLocation) {
var tests = JSON.parse(fs.readFileSync(testLocation).toString());
var suiteName = path.join(path.basename(path.dirname(testLocation)), path.basename(testLocation));
tests.forEach(function (test, index) {
manager.registerTest(suiteName + ' > ' + test.desc, function (ctx) {
var locator = {
getFilePath: function (scopeName) { return null; },
getInjections: function (scopeName) {
if (scopeName === test.grammarScopeName) {
return test.grammarInjections;
}
return void 0;
}
};
var registry = new main_1.Registry(locator);
var grammar = null;
test.grammars.forEach(function (grammarPath) {
var tmpGrammar = registry.loadGrammarFromPathSync(path.join(path.dirname(testLocation), grammarPath));
if (test.grammarPath === grammarPath) {
grammar = tmpGrammar;
}
});
if (test.grammarScopeName) {
grammar = registry.grammarForScopeName(test.grammarScopeName);
}
var prevState = null;
if (!grammar) {
ctx.fail('I HAVE NO GRAMMAR FOR TEST');
return;
}
for (var i = 0; i < test.lines.length; i++) {
prevState = assertTokenization(ctx, grammar, test.lines[i], prevState);
}
});
if (test.grammarScopeName) {
grammar = registry.grammarForScopeName(test.grammarScopeName);
}
var prevState = null;
if (!grammar) {
console.error('I HAVE NO GRAMMAR FOR TEST ' + desc);
return;
}
for (var i = 0; i < test.lines.length; i++) {
prevState = assertTokenization(noAsserts, grammar, test.lines[i], prevState, desc);
}
});
if (errCnt === 0) {
var msg = 'Test suite at ' + testLocation + ' finished ok';
console.log(msg.green);
}
else {
var msg = 'Test suite at ' + testLocation + ' finished with ' + errCnt + ' errors.';
console.log(msg.red);
}
}
exports.runDescriptiveTests = runDescriptiveTests;
function assertTokenization(noAsserts, grammar, testCase, prevState, desc) {
function assertTokenization(ctx, grammar, testCase, prevState) {
var r = grammar.tokenizeLine(testCase.line, prevState);
if (!noAsserts) {
assertTokens(r.tokens, testCase.line, testCase.tokens, desc);
}
assertTokens(ctx, r.tokens, testCase.line, testCase.tokens);
return r.ruleStack;
}
function fail(message, actual, expected) {
errCnt++;
console.error(message.red);
console.log(JSON.stringify(actual, null, '\t'));
console.log(JSON.stringify(expected, null, '\t'));
}
function assertTokens(actual, line, expected, desc) {
function assertTokens(ctx, actual, line, expected) {
var actualTokens = actual.map(function (token) {
return {
value: line.substring(token.startIndex, token.endIndex),
@ -80,32 +149,32 @@ function assertTokens(actual, line, expected, desc) {
});
}
if (actualTokens.length !== expected.length) {
fail('test: GOT DIFFERENT LENGTHS FOR ' + desc, actualTokens, expected);
ctx.fail('GOT DIFFERENT LENGTHS FOR ', actualTokens, expected);
return;
}
for (var i = 0, len = actualTokens.length; i < len; i++) {
assertToken(actualTokens[i], expected[i], desc);
assertToken(ctx, actualTokens[i], expected[i]);
}
}
function assertToken(actual, expected, desc) {
function assertToken(ctx, actual, expected) {
if (actual.value !== expected.value) {
fail('test: GOT DIFFERENT VALUES FOR ' + desc, actual.value, expected.value);
ctx.fail('test: GOT DIFFERENT VALUES FOR ', actual.value, expected.value);
return;
}
if (actual.scopes.length !== expected.scopes.length) {
fail('test: GOT DIFFERENT scope lengths FOR ' + desc, actual.scopes, expected.scopes);
ctx.fail('test: GOT DIFFERENT scope lengths FOR ', actual.scopes, expected.scopes);
return;
}
for (var i = 0, len = actual.scopes.length; i < len; i++) {
if (actual.scopes[i] !== expected.scopes[i]) {
fail('test: GOT DIFFERENT scopes FOR ' + desc, actual.scopes, expected.scopes);
ctx.fail('test: GOT DIFFERENT scopes FOR ', actual.scopes, expected.scopes);
return;
}
}
}
function runMatcherTests(testLocation, testNum) {
if (testNum === void 0) { testNum = -1; }
function generateMatcherTests(manager, testLocation) {
var tests = JSON.parse(fs.readFileSync(testLocation).toString());
var suiteName = path.join(path.basename(path.dirname(testLocation)), path.basename(testLocation));
var nameMatcher = function (identifers, stackElements) {
var lastIndex = 0;
return identifers.every(function (identifier) {
@ -118,29 +187,13 @@ function runMatcherTests(testLocation, testNum) {
return false;
});
};
var errCnt = 0;
tests.forEach(function (test, index) {
if (testNum !== -1 && testNum !== index) {
return;
}
var matcher = main_1.createMatcher(test.expression, nameMatcher);
var result = matcher(test.input);
if (result === test.result) {
console.log(index + ': passed');
}
else {
var message = index + ': failed , expected ' + test.result;
console.error(message.red);
errCnt++;
}
manager.registerTest(suiteName + ' > ' + index, function (ctx) {
var matcher = main_1.createMatcher(test.expression, nameMatcher);
var result = matcher(test.input);
if (result !== test.result) {
ctx.fail('matcher expected', result, test.result);
}
});
});
if (errCnt === 0) {
var msg = 'Test suite at ' + testLocation + ' finished ok';
console.log(msg.green);
}
else {
var msg = 'Test suite at ' + testLocation + ' finished with ' + errCnt + ' errors.';
console.log(msg.red);
}
}
exports.runMatcherTests = runMatcherTests;

Просмотреть файл

@ -486,13 +486,19 @@ function _tokenizeString(grammar: Grammar, lineText: OnigString, isFirstLine: bo
lineTokens.produce(stack, captureIndices[0].end);
// pop
stack.pop();
let popped = stack.pop();
if (!hasAdvanced && stackElement.enterPos === linePos) {
// Grammar pushed & popped a rule without advancing
console.error('Grammar is in an endless loop - case 1');
console.error('[1] - Grammar is in an endless loop - Grammar pushed & popped a rule without advancing');
// See https://github.com/Microsoft/vscode-textmate/issues/12
// Let's assume this was a mistake by the grammar author and the intent was to continue in this state
stack.push(popped);
lineTokens.produce(stack, lineLength);
linePos = lineLength;
return false;
}
} else if (matchedRuleId === -3) {
@ -523,7 +529,7 @@ function _tokenizeString(grammar: Grammar, lineText: OnigString, isFirstLine: bo
if (!hasAdvanced && stackElement.ruleId === stack[stack.length - 1].ruleId) {
// Grammar pushed the same rule without advancing
console.error('Grammar is in an endless loop - case 2');
console.error('[2] - Grammar is in an endless loop - Grammar pushed the same rule without advancing');
stack.pop();
lineTokens.produce(stack, lineLength);
linePos = lineLength;
@ -543,7 +549,7 @@ function _tokenizeString(grammar: Grammar, lineText: OnigString, isFirstLine: bo
if (!hasAdvanced && stackElement.ruleId === stack[stack.length - 1].ruleId) {
// Grammar pushed the same rule without advancing
console.error('Grammar is in an endless loop - case 2');
console.error('[3] - Grammar is in an endless loop - Grammar pushed the same rule without advancing');
stack.pop();
lineTokens.produce(stack, lineLength);
linePos = lineLength;
@ -560,7 +566,7 @@ function _tokenizeString(grammar: Grammar, lineText: OnigString, isFirstLine: bo
if (!hasAdvanced) {
// Grammar is not advancing, nor is it pushing/popping
console.error('Grammar is in an endless loop - case 3');
console.error('[4] - Grammar is in an endless loop - Grammar is not advancing, nor is it pushing/popping');
if (stack.length > 1) {
stack.pop();
}

Просмотреть файл

@ -9,61 +9,157 @@ import {Registry, createMatcher, IGrammarLocator} from '../main';
import {IToken, StackElement, IGrammar} from '../grammar';
import 'colors';
var errCnt = 0;
enum TestResult {
Pending,
Success,
Failed
}
export function runDescriptiveTests(testLocation: string) {
let tests:IRawTest[] = JSON.parse(fs.readFileSync(testLocation).toString());
class Test {
testName:string;
result:TestResult;
failReason: string;
errCnt = 0;
tests.forEach(function(test, index) {
let desc = test.desc;
let noAsserts = (test.feature === 'endless-loop');
private _runner: (ctx:TestContext) => void;
console.log(index + ' - RUNNING ' + desc);
let locator : IGrammarLocator = {
getFilePath: (scopeName:string) => null,
getInjections: (scopeName:string) => {
if (scopeName === test.grammarScopeName) {
return test.grammarInjections;
}
return void 0;
}
}
let registry = new Registry(locator);
let grammar: IGrammar = null;
test.grammars.forEach(function(grammarPath) {
let tmpGrammar = registry.loadGrammarFromPathSync(path.join(path.dirname(testLocation), grammarPath));
if (test.grammarPath === grammarPath) {
grammar = tmpGrammar;
}
});
if (test.grammarScopeName) {
grammar = registry.grammarForScopeName(test.grammarScopeName);
}
let prevState: StackElement[] = null;
if (!grammar) {
console.error('I HAVE NO GRAMMAR FOR TEST ' + desc);
return;
}
for (let i = 0; i < test.lines.length; i++) {
prevState = assertTokenization(noAsserts, grammar, test.lines[i], prevState, desc);
}
});
if (errCnt === 0) {
var msg = 'Test suite at ' + testLocation + ' finished ok';
console.log((<any>msg).green);
} else {
var msg = 'Test suite at ' + testLocation + ' finished with ' + errCnt + ' errors.';
console.log((<any>msg).red);
constructor(testName:string, runner: (ctx:TestContext) => void) {
this.testName = testName;
this._runner = runner;
this.result = TestResult.Pending;
this.failReason = null;
}
public run(): void {
let ctx = new TestContext(this);
try {
this.result = TestResult.Success;
this._runner(ctx);
} catch(err) {
this.result = TestResult.Failed;
this.failReason = err;
}
}
public fail<T>(message:string, actual:T, expected:T): void {
this.result = TestResult.Failed;
let reason = [
message
];
if (actual) {
'ACTUAL: ' + reason.push(JSON.stringify(actual, null, '\t'))
}
if (expected) {
'EXPECTED: ' + reason.push(JSON.stringify(expected, null, '\t'))
}
this.failReason = reason.join('\n');
}
}
class TestContext {
private _test: Test;
constructor(test: Test) {
this._test = test;
}
public fail<T>(message:string, actual?:T, expected?:T): void {
this._test.fail(message, actual, expected);
}
}
class TestManager {
private _tests:Test[];
constructor() {
this._tests = [];
}
registerTest(testName: string, runner:(ctx:TestContext)=>void): void {
this._tests.push(new Test(testName, runner));
}
runTests(): void {
let len = this._tests.length;
for (let i = 0; i < len; i++) {
let test = this._tests[i];
let progress = (i + 1) + '/' + len;
console.log((<any>progress).yellow, ': ' + test.testName);
test.run();
if (test.result === TestResult.Failed) {
console.log((<any>'FAILED').red);
console.log(test.failReason);
}
}
let passed = this._tests.filter(t => t.result === TestResult.Success);
if (passed.length === len) {
console.log((<any>(passed.length + '/' + len + ' PASSED.')).green);
} else {
console.log((<any>(passed.length + '/' + len + ' PASSED.')).red);
}
}
}
export function runTests(tokenizationTestPaths:string[], matcherTestPaths:string[]): void {
let manager = new TestManager();
tokenizationTestPaths.forEach((path) => {
generateTokenizationTests(manager, path)
});
matcherTestPaths.forEach((path) => {
generateMatcherTests(manager, path);
});
manager.runTests();
}
function generateTokenizationTests(manager:TestManager, testLocation: string): void {
let tests:IRawTest[] = JSON.parse(fs.readFileSync(testLocation).toString());
let suiteName = path.join(path.basename(path.dirname(testLocation)), path.basename(testLocation));
tests.forEach(function(test, index) {
manager.registerTest(suiteName + ' > ' + test.desc, (ctx) => {
let locator : IGrammarLocator = {
getFilePath: (scopeName:string) => null,
getInjections: (scopeName:string) => {
if (scopeName === test.grammarScopeName) {
return test.grammarInjections;
}
return void 0;
}
}
let registry = new Registry(locator);
let grammar: IGrammar = null;
test.grammars.forEach(function(grammarPath) {
let tmpGrammar = registry.loadGrammarFromPathSync(path.join(path.dirname(testLocation), grammarPath));
if (test.grammarPath === grammarPath) {
grammar = tmpGrammar;
}
});
if (test.grammarScopeName) {
grammar = registry.grammarForScopeName(test.grammarScopeName);
}
let prevState: StackElement[] = null;
if (!grammar) {
ctx.fail('I HAVE NO GRAMMAR FOR TEST');
return;
}
for (let i = 0; i < test.lines.length; i++) {
prevState = assertTokenization(ctx, grammar, test.lines[i], prevState);
}
});
});
}
interface IRawTest {
desc: string;
feature: string;
grammars: string[];
grammarPath?: string;
grammarScopeName?: string;
@ -81,22 +177,13 @@ interface IRawToken {
scopes: string[];
}
function assertTokenization(noAsserts:boolean, grammar:IGrammar, testCase:IRawTestLine, prevState: StackElement[], desc:string): StackElement[] {
function assertTokenization(ctx:TestContext, grammar:IGrammar, testCase:IRawTestLine, prevState: StackElement[]): StackElement[] {
let r = grammar.tokenizeLine(testCase.line, prevState);
if (!noAsserts) {
assertTokens(r.tokens, testCase.line, testCase.tokens, desc);
}
assertTokens(ctx, r.tokens, testCase.line, testCase.tokens);
return r.ruleStack;
}
function fail<T>(message:string, actual:T, expected:T): void {
errCnt++;
console.error((<any>message).red);
console.log(JSON.stringify(actual, null, '\t'));
console.log(JSON.stringify(expected, null, '\t'));
}
function assertTokens(actual:IToken[], line:string, expected:IRawToken[], desc:string): void {
function assertTokens(ctx:TestContext, actual:IToken[], line:string, expected:IRawToken[]): void {
let actualTokens:IRawToken[] = actual.map(function(token) {
return {
value: line.substring(token.startIndex, token.endIndex),
@ -110,26 +197,26 @@ function assertTokens(actual:IToken[], line:string, expected:IRawToken[], desc:s
});
}
if (actualTokens.length !== expected.length) {
fail('test: GOT DIFFERENT LENGTHS FOR ' + desc, actualTokens, expected);
ctx.fail('GOT DIFFERENT LENGTHS FOR ', actualTokens, expected);
return;
}
for (let i = 0, len = actualTokens.length; i < len; i++) {
assertToken(actualTokens[i], expected[i], desc);
assertToken(ctx, actualTokens[i], expected[i]);
}
}
function assertToken(actual:IRawToken, expected:IRawToken, desc:string): void {
function assertToken(ctx:TestContext, actual:IRawToken, expected:IRawToken): void {
if (actual.value !== expected.value) {
fail('test: GOT DIFFERENT VALUES FOR ' + desc, actual.value, expected.value);
ctx.fail('test: GOT DIFFERENT VALUES FOR ', actual.value, expected.value);
return;
}
if (actual.scopes.length !== expected.scopes.length) {
fail('test: GOT DIFFERENT scope lengths FOR ' + desc, actual.scopes, expected.scopes);
ctx.fail('test: GOT DIFFERENT scope lengths FOR ', actual.scopes, expected.scopes);
return;
}
for (let i = 0, len = actual.scopes.length; i < len; i++) {
if (actual.scopes[i] !== expected.scopes[i]) {
fail('test: GOT DIFFERENT scopes FOR ' + desc, actual.scopes, expected.scopes);
ctx.fail('test: GOT DIFFERENT scopes FOR ', actual.scopes, expected.scopes);
return;
}
}
@ -141,8 +228,9 @@ interface IMatcherTest {
result: boolean;
}
export function runMatcherTests(testLocation: string, testNum =-1) {
function generateMatcherTests(manager:TestManager, testLocation: string) {
let tests:IMatcherTest[] = JSON.parse(fs.readFileSync(testLocation).toString());
let suiteName = path.join(path.basename(path.dirname(testLocation)), path.basename(testLocation));
var nameMatcher = (identifers: string[], stackElements: string[]) => {
var lastIndex = 0;
@ -156,27 +244,13 @@ export function runMatcherTests(testLocation: string, testNum =-1) {
return false;
});
};
var errCnt = 0;
tests.forEach((test, index) => {
if (testNum !== -1 && testNum !== index) {
return;
}
var matcher = createMatcher(test.expression, nameMatcher);
var result = matcher(test.input);
if (result === test.result) {
console.log(index + ': passed');
} else {
var message = index + ': failed , expected ' + test.result;
console.error((<any>message).red);
errCnt++;
}
manager.registerTest(suiteName + ' > ' + index, (ctx) => {
var matcher = createMatcher(test.expression, nameMatcher);
var result = matcher(test.input);
if (result !== test.result) {
ctx.fail('matcher expected', result, test.result);
}
});
});
if (errCnt === 0) {
var msg = 'Test suite at ' + testLocation + ' finished ok';
console.log((<any>msg).green);
} else {
var msg = 'Test suite at ' + testLocation + ' finished with ' + errCnt + ' errors.';
console.log((<any>msg).red);
}
}

Просмотреть файл

@ -2680,7 +2680,21 @@
"grammarPath": "fixtures/infinite-loop.json",
"lines": [
{
"line": "abc"
"line": "abc",
"tokens": [{
"value": "a",
"scopes": [
"source.infinite-loop",
"start"
]
},
{
"value": "bc",
"scopes": [
"source.infinite-loop"
]
}]
}
],
"grammars": [
@ -2696,8 +2710,7 @@
"fixtures/python-regex.json",
"fixtures/infinite-loop.json"
],
"desc": "TEST #38",
"feature": "endless-loop"
"desc": "TEST #38"
},
{
"grammarScopeName": "source.css.scss",
@ -3361,7 +3374,14 @@
"grammarPath": "fixtures/forever.json",
"lines": [
{
"line": "forever and ever"
"line": "forever and ever",
"tokens": [{
"value": "forever and ever",
"scopes": [
"source.forever",
"text"
]
}]
}
],
"grammars": [
@ -3377,8 +3397,7 @@
"fixtures/python-regex.json",
"fixtures/forever.json"
],
"desc": "TEST #48",
"feature": "endless-loop"
"desc": "TEST #48"
},
{
"grammarScopeName": "source.ruby",
@ -5246,22 +5265,216 @@
"grammarScopeName": "source.thrift",
"lines": [
{
"line": "exception SimpleErr {"
"line": "exception SimpleErr {",
"tokens": [{
"value": "exception",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"keyword.other.exception.thrift"
]
},
{
"value": " ",
"scopes": [
"source.thrift",
"meta.exception.thrift"
]
},
{
"value": "SimpleErr",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"entity.name.type.exception.thrift"
]
},
{
"value": " ",
"scopes": [
"source.thrift",
"meta.exception.thrift"
]
},
{
"value": "{",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"punctuation.section.exception.begin.thrift"
]
}]
},
{
"line": " 1: string message"
"line": " 1: string message",
"tokens": [{
"value": " ",
"scopes": [
"source.thrift",
"meta.exception.thrift"
]
},
{
"value": "1:",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift",
"entity.other.field-id.thrift"
]
},
{
"value": " ",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift"
]
},
{
"value": "string",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift",
"storage.type.field.thrift"
]
},
{
"value": " ",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift"
]
},
{
"value": "message",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift",
"variable.other.field-name.thrift"
]
}]
},
{
"line": ""
"line": "",
"tokens": [{
"value": "",
"scopes": [
"source.thrift",
"meta.exception.thrift"
]
}]
},
{
"line": "service SimpleService {"
"line": "service SimpleService {",
"tokens": [{
"value": "service",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift",
"storage.type.field.thrift"
]
},
{
"value": " ",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift"
]
},
{
"value": "SimpleService",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift",
"variable.other.field-name.thrift"
]
},
{
"value": " ",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift"
]
},
{
"value": "{",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift"
]
}]
},
{
"line": " void Simple() throws (1: SimpleErr simpleErr)"
"line": " void Simple() throws (1: SimpleErr simpleErr)",
"tokens": [{
"value": " ",
"scopes": [
"source.thrift",
"meta.exception.thrift"
]
},
{
"value": "void",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift",
"storage.type.field.thrift"
]
},
{
"value": " ",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift"
]
},
{
"value": "Simple",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift",
"variable.other.field-name.thrift"
]
},
{
"value": "(",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift"
]
},
{
"value": ") throws (1: SimpleErr simpleErr)",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift"
]
}]
},
{
"line": "}"
"line": "}",
"tokens": [{
"value": "}",
"scopes": [
"source.thrift",
"meta.exception.thrift",
"meta.field.thrift"
]
}]
}
],
"grammars": [
@ -5277,14 +5490,19 @@
"fixtures/python-regex.json",
"fixtures/thrift.json"
],
"desc": "TEST #73",
"feature": "endless-loop"
"desc": "TEST #73"
},
{
"grammarScopeName": "source.loops",
"lines": [
{
"line": "test"
"line": "test",
"tokens": [{
"value": "test",
"scopes": [
"source.loops"
]
}]
}
],
"grammars": [
@ -5300,7 +5518,6 @@
"fixtures/python-regex.json",
"fixtures/loops.json"
],
"desc": "TEST #74",
"feature": "endless-loop"
"desc": "TEST #74"
}
]

Просмотреть файл

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>fileTypes</key>
<array>
<string>testlang</string>
</array>
<key>name</key>
<string>testlang</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#indentedVerbatimOp</string>
</dict>
</array>
<key>repository</key>
<dict>
<key>indentedVerbatimOp</key>
<dict>
<key>begin</key>
<string>^([ \t]*)(?=(.*?)\|$)</string>
<key>end</key>
<string>^(?!\1[ \t])(?=[ \t]*\S)</string>
<key>name</key>
<string>string.unquoted.verbatim.youki</string>
</dict>
</dict>
<key>scopeName</key>
<string>text.testlang</string>
<key>uuid</key>
<string>159375af-d9b4-448c-a9da-d235eadf3556</string>
</dict>
</plist>

Просмотреть файл

@ -161,6 +161,7 @@
"fixtures/aspvbnet.plist"
],
"grammarPath": "fixtures/aspvbnet.plist",
"desc": "asp",
"lines": [
{
"line": "Dim tmpStr As String",
@ -224,6 +225,7 @@
"fixtures/aspvbnet.plist"
],
"grammarPath": "fixtures/aspvbnet.plist",
"desc": "asp2",
"lines": [
{
"line": "Dim tmpStr As String",
@ -848,5 +850,72 @@
]
}
]
},
{
"grammars": [
"fixtures/testlang12.plist"
],
"grammarPath": "fixtures/testlang12.plist",
"desc": "Issue #12",
"lines": [
{
"line": "[test]|",
"tokens": [{
"value": "[test]|",
"scopes": [
"text.testlang",
"string.unquoted.verbatim.youki"
]
}]
},
{
"line": "\tverb",
"tokens": [{
"value": "\tverb",
"scopes": [
"text.testlang",
"string.unquoted.verbatim.youki"
]
}]
},
{
"line": "asd",
"tokens": [{
"value": "asd",
"scopes": [
"text.testlang"
]
}]
},
{
"line": "asd",
"tokens": [{
"value": "asd",
"scopes": [
"text.testlang"
]
}]
},
{
"line": "[test]|",
"tokens": [{
"value": "[test]|",
"scopes": [
"text.testlang",
"string.unquoted.verbatim.youki"
]
}]
},
{
"line": "\tverbatim",
"tokens": [{
"value": "\tverbatim",
"scopes": [
"text.testlang",
"string.unquoted.verbatim.youki"
]
}]
}
]
}
]