[json] folding tests

This commit is contained in:
Martin Aeschlimann
2018-03-09 12:11:42 +01:00
parent 35f93d5423
commit 2b86473bdc
6 changed files with 215 additions and 83 deletions

View File

@@ -16,7 +16,8 @@
"vscode-uri": "^1.0.3"
},
"devDependencies": {
"@types/node": "7.0.43"
"@types/node": "7.0.43",
"@types/mocha": "2.2.33"
},
"scripts": {
"compile": "gulp compile-extension:json-server",
@@ -24,6 +25,7 @@
"install-service-next": "yarn add vscode-json-languageservice@next",
"install-service-local": "yarn link vscode-json-languageservice",
"install-server-next": "yarn add vscode-languageserver@next",
"install-server-local": "yarn link vscode-languageserver-server"
"install-server-local": "yarn link vscode-languageserver-server",
"test": "npm run compile && ../../../node_modules/.bin/mocha"
}
}

View File

@@ -0,0 +1,86 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TextDocument, Position } from 'vscode-languageserver';
import { createScanner, SyntaxKind, ScanError } from 'jsonc-parser';
import { FoldingRangeType, FoldingRange, FoldingRangeList } from './protocol/foldingProvider.proposed';
export function getFoldingRegions(document: TextDocument) {
let ranges: FoldingRange[] = [];
let stack: FoldingRange[] = [];
let prevStart = -1;
let scanner = createScanner(document.getText(), false);
let token = scanner.scan();
while (token !== SyntaxKind.EOF) {
switch (token) {
case SyntaxKind.OpenBraceToken:
case SyntaxKind.OpenBracketToken: {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let range = { startLine, endLine: startLine, type: token === SyntaxKind.OpenBraceToken ? 'object' : 'array' };
stack.push(range);
break;
}
case SyntaxKind.CloseBraceToken:
case SyntaxKind.CloseBracketToken: {
let type = token === SyntaxKind.CloseBraceToken ? 'object' : 'array';
if (stack.length > 0 && stack[stack.length - 1].type === type) {
let range = stack.pop();
let line = document.positionAt(scanner.getTokenOffset()).line;
if (range && line > range.startLine + 1 && prevStart !== range.startLine) {
range.endLine = line - 1;
ranges.push(range);
prevStart = range.startLine;
}
}
break;
}
case SyntaxKind.BlockCommentTrivia: {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
if (scanner.getTokenError() === ScanError.UnexpectedEndOfComment && startLine + 1 < document.lineCount) {
scanner.setPosition(document.offsetAt(Position.create(startLine + 1, 0)));
} else {
if (startLine < endLine) {
ranges.push({ startLine, endLine, type: FoldingRangeType.Comment });
prevStart = startLine;
}
}
break;
}
case SyntaxKind.LineCommentTrivia: {
let text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength());
let m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/);
if (m) {
let line = document.positionAt(scanner.getTokenOffset()).line;
if (m[1]) { // start pattern match
let range = { startLine: line, endLine: line, type: FoldingRangeType.Region };
stack.push(range);
} else {
let i = stack.length - 1;
while (i >= 0 && stack[i].type !== FoldingRangeType.Region) {
i--;
}
if (i >= 0) {
let range = stack[i];
stack.length = i;
if (line > range.startLine && prevStart !== range.startLine) {
range.endLine = line;
ranges.push(range);
prevStart = range.startLine;
}
}
}
}
break;
}
}
token = scanner.scan();
}
return <FoldingRangeList>{ ranges };
}

View File

@@ -7,20 +7,20 @@
import {
createConnection, IConnection,
TextDocuments, TextDocument, InitializeParams, InitializeResult, NotificationType, RequestType,
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, DocumentColorRequest, ColorPresentationRequest, Position,
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, DocumentColorRequest, ColorPresentationRequest
} from 'vscode-languageserver';
import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light';
import fs = require('fs');
import * as fs from 'fs';
import URI from 'vscode-uri';
import * as URL from 'url';
import Strings = require('./utils/strings');
import { startsWith } from './utils/strings';
import { formatError, runSafe, runSafeAsync } from './utils/errors';
import { JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration } from 'vscode-json-languageservice';
import { getLanguageModelCache } from './languageModelCache';
import { createScanner, SyntaxKind, ScanError } from 'jsonc-parser';
import { getFoldingRegions } from './folding';
import { FoldingRangeType, FoldingRangesRequest, FoldingRange, FoldingRangeList, FoldingProviderServerCapabilities } from './protocol/foldingProvider.proposed';
import { FoldingRangesRequest, FoldingProviderServerCapabilities } from './protocol/foldingProvider.proposed';
interface ISchemaAssociations {
[pattern: string]: string[];
@@ -93,14 +93,14 @@ let workspaceContext = {
};
let schemaRequestService = (uri: string): Thenable<string> => {
if (Strings.startsWith(uri, 'file://')) {
if (startsWith(uri, 'file://')) {
let fsPath = URI.parse(uri).fsPath;
return new Promise<string>((c, e) => {
fs.readFile(fsPath, 'UTF-8', (err, result) => {
err ? e('') : c(result.toString());
});
});
} else if (Strings.startsWith(uri, 'vscode://')) {
} else if (startsWith(uri, 'vscode://')) {
return connection.sendRequest(VSCodeContentRequest.type, uri).then(responseText => {
return responseText;
}, error => {
@@ -361,80 +361,7 @@ connection.onRequest(FoldingRangesRequest.type, params => {
return runSafe(() => {
let document = documents.get(params.textDocument.uri);
if (document) {
let ranges: FoldingRange[] = [];
let stack: FoldingRange[] = [];
let prevStart = -1;
let scanner = createScanner(document.getText(), false);
let token = scanner.scan();
while (token !== SyntaxKind.EOF) {
switch (token) {
case SyntaxKind.OpenBraceToken:
case SyntaxKind.OpenBracketToken: {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let range = { startLine, endLine: startLine, type: token === SyntaxKind.OpenBraceToken ? 'object' : 'array' };
stack.push(range);
break;
}
case SyntaxKind.CloseBraceToken:
case SyntaxKind.CloseBracketToken: {
let type = token === SyntaxKind.CloseBraceToken ? 'object' : 'array';
if (stack.length > 0 && stack[stack.length - 1].type === type) {
let range = stack.pop();
let line = document.positionAt(scanner.getTokenOffset()).line;
if (range && line > range.startLine + 1 && prevStart !== range.startLine) {
range.endLine = line - 1;
ranges.push(range);
prevStart = range.startLine;
}
}
break;
}
case SyntaxKind.BlockCommentTrivia: {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
if (scanner.getTokenError() === ScanError.UnexpectedEndOfComment && startLine + 1 < document.lineCount) {
scanner.setPosition(document.offsetAt(Position.create(startLine + 1, 0)));
} else {
if (startLine < endLine) {
ranges.push({ startLine, endLine, type: FoldingRangeType.Comment });
prevStart = startLine;
}
}
break;
}
case SyntaxKind.LineCommentTrivia: {
let text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength());
let m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/);
if (m) {
let line = document.positionAt(scanner.getTokenOffset()).line;
if (m[1]) { // start pattern match
let range = { startLine: line, endLine: line, type: FoldingRangeType.Region };
stack.push(range);
} else {
let i = stack.length - 1;
while (i >= 0 && stack[i].type !== FoldingRangeType.Region) {
i--;
}
if (i >= 0) {
let range = stack[i];
stack.length = i;
if (line > range.startLine && prevStart !== range.startLine) {
range.endLine = line;
ranges.push(range);
prevStart = range.startLine;
}
}
}
}
break;
}
}
token = scanner.scan();
}
return <FoldingRangeList>{ ranges };
return getFoldingRegions(document);
}
return null;
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`);

View File

@@ -0,0 +1,110 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'mocha';
import * as assert from 'assert';
import { TextDocument } from 'vscode-languageserver';
import { getFoldingRegions } from '../folding';
interface ExpectedIndentRange {
startLine: number;
endLine: number;
type?: string;
}
function assertRanges(lines: string[], expected: ExpectedIndentRange[]): void {
let document = TextDocument.create('test://foo/bar.json', 'json', 1, lines.join('\n'));
let actual = getFoldingRegions(document).ranges;
let actualRanges = [];
for (let i = 0; i < actual.length; i++) {
actualRanges[i] = r(actual[i].startLine, actual[i].endLine, actual[i].type);
}
actualRanges = actualRanges.sort((r1, r2) => r1.startLine - r2.startLine);
assert.deepEqual(actualRanges, expected);
}
function r(startLine: number, endLine: number, type?: string): ExpectedIndentRange {
return { startLine, endLine, type };
}
suite('Object Folding', () => {
test('Fold one level', () => {
let range = [
/*0*/'{',
/*1*/'"foo":"bar"',
/*2*/'}'
];
assertRanges(range, [r(0, 1, 'object')]);
});
test('Fold two level', () => {
let range = [
/*0*/'[',
/*1*/'{',
/*2*/'"foo":"bar"',
/*3*/'}',
/*4*/']'
];
assertRanges(range, [r(0, 3, 'array'), r(1, 2, 'object')]);
});
test('Fold Arrays', () => {
let range = [
/*0*/'[',
/*1*/'[',
/*2*/'],[',
/*3*/'1',
/*4*/']',
/*5*/']'
];
assertRanges(range, [r(0, 4, 'array'), r(2, 3, 'array')]);
});
test('Filter start on same line', () => {
let range = [
/*0*/'[[',
/*1*/'[',
/*2*/'],[',
/*3*/'1',
/*4*/']',
/*5*/']]'
];
assertRanges(range, [r(0, 4, 'array'), r(2, 3, 'array')]);
});
test('Fold commment', () => {
let range = [
/*0*/'/*',
/*1*/' multi line',
/*2*/'*/',
];
assertRanges(range, [r(0, 2, 'comment')]);
});
test('Incomplete commment', () => {
let range = [
/*0*/'/*',
/*1*/'{',
/*2*/'"foo":"bar"',
/*3*/'}',
];
assertRanges(range, [r(1, 2, 'object')]);
});
test('Fold regions', () => {
let range = [
/*0*/'// #region',
/*1*/'{',
/*2*/'}',
/*3*/'// #endregion',
];
assertRanges(range, [r(0, 3, 'region')]);
});
});

View File

@@ -0,0 +1,3 @@
--ui tdd
--useColors true
./out/test/**/*.test.js

View File

@@ -2,6 +2,10 @@
# yarn lockfile v1
"@types/mocha@2.2.33":
version "2.2.33"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def"
"@types/node@7.0.43":
version "7.0.43"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"