mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-28 04:23:32 +01:00
[json] folding tests
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
86
extensions/json/server/src/folding.ts
Normal file
86
extensions/json/server/src/folding.ts
Normal 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 };
|
||||
}
|
||||
@@ -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}`);
|
||||
|
||||
110
extensions/json/server/src/test/folding.test.ts
Normal file
110
extensions/json/server/src/test/folding.test.ts
Normal 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')]);
|
||||
});
|
||||
|
||||
});
|
||||
3
extensions/json/server/test/mocha.opts
Normal file
3
extensions/json/server/test/mocha.opts
Normal file
@@ -0,0 +1,3 @@
|
||||
--ui tdd
|
||||
--useColors true
|
||||
./out/test/**/*.test.js
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user