Remove linesModel, use new APIs

This commit is contained in:
Martin Aeschlimann 2016-01-22 17:33:02 +01:00
parent 2747ebef95
commit 8bccda9e42
11 changed files with 38 additions and 238 deletions

View file

@ -13,8 +13,6 @@ import nls = require('./utils/nls');
import {CompletionItem, CompletionItemKind, CompletionOptions, ITextDocument, TextDocumentIdentifier, TextDocumentPosition, Range, TextEdit} from 'vscode-languageserver';
import {LinesModel} from './utils/lines';
export interface ISuggestionsCollector {
add(suggestion: CompletionItem): void;
error(message: string): void;
@ -28,16 +26,16 @@ export class JSONCompletion {
this.schemaService = schemaService;
}
public doSuggest(document: ITextDocument, textDocumentPosition: TextDocumentPosition, lines: LinesModel, doc: Parser.JSONDocument): Thenable<CompletionItem[]> {
public doSuggest(document: ITextDocument, textDocumentPosition: TextDocumentPosition, doc: Parser.JSONDocument): Thenable<CompletionItem[]> {
var offset = lines.offsetAt(textDocumentPosition.position);
var offset = document.offsetAt(textDocumentPosition.position);
var node = doc.getNodeFromOffsetEndInclusive(offset);
var overwriteRange = null;
var result: CompletionItem[] = [];
if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
overwriteRange = Range.create(lines.positionAt(node.start), lines.positionAt(node.end));
overwriteRange = Range.create(document.positionAt(node.start), document.positionAt(node.end));
}
var proposed: { [key: string]: boolean } = {};

View file

@ -10,14 +10,12 @@ import Strings = require('./utils/strings');
import {SymbolInformation, SymbolKind, ITextDocument, Range, Location} from 'vscode-languageserver';
import {LinesModel} from './utils/lines';
export class JSONDocumentSymbols {
constructor() {
}
public compute(document: ITextDocument, lines: LinesModel, doc: Parser.JSONDocument): Promise<SymbolInformation[]> {
public compute(document: ITextDocument, doc: Parser.JSONDocument): Promise<SymbolInformation[]> {
let root = doc.root;
if (!root) {
@ -33,7 +31,7 @@ export class JSONDocumentSymbols {
if (item.type === 'object') {
let property = (<Parser.ObjectASTNode>item).getFirstProperty('key');
if (property && property.value) {
let location = Location.create(document.uri, Range.create(lines.positionAt(item.start), lines.positionAt(item.end)));
let location = Location.create(document.uri, Range.create(document.positionAt(item.start), document.positionAt(item.end)));
result.push({ name: property.value.getValue(), kind: SymbolKind.Function, location: location });
}
}
@ -51,7 +49,7 @@ export class JSONDocumentSymbols {
let objectNode = <Parser.ObjectASTNode>node;
objectNode.properties.forEach((property: Parser.PropertyASTNode) => {
let location = Location.create(document.uri, Range.create(lines.positionAt(property.start), lines.positionAt(property.end)));
let location = Location.create(document.uri, Range.create(document.positionAt(property.start), document.positionAt(property.end)));
let valueNode = property.value;
if (valueNode) {
let childContainerName = containerName ? containerName + '.' + property.key.name : property.key.name;

View file

@ -6,32 +6,31 @@
import Json = require('./json-toolbox/json');
import {ITextDocument, Range, Position, FormattingOptions, TextEdit} from 'vscode-languageserver';
import {LinesModel} from './utils/lines';
export function format(document: ITextDocument, lines: LinesModel, range: Range, options: FormattingOptions): TextEdit[] {
export function format(document: ITextDocument, range: Range, options: FormattingOptions): TextEdit[] {
const documentText = document.getText();
let initialIndentLevel: number;
let value: string;
let rangeOffset: number;
if (range) {
let startPosition = Position.create(range.start.line, 0);
rangeOffset = lines.offsetAt(startPosition);
rangeOffset = document.offsetAt(startPosition);
let endOffset = lines.offsetAt(Position.create(range.end.line + 1, 0));
let endLineStart = lines.offsetAt(Position.create(range.end.line, 0));
let endOffset = document.offsetAt(Position.create(range.end.line + 1, 0));
let endLineStart = document.offsetAt(Position.create(range.end.line, 0));
while (endOffset > endLineStart && isEOL(documentText, endOffset - 1)) {
endOffset--;
}
range = Range.create(startPosition, lines.positionAt(endOffset));
range = Range.create(startPosition, document.positionAt(endOffset));
value = documentText.substring(rangeOffset, endOffset);
initialIndentLevel = computeIndentLevel(value, 0, options);
} else {
value = documentText;
range = Range.create(Position.create(0, 0), lines.positionAt(value.length));
range = Range.create(Position.create(0, 0), document.positionAt(value.length));
initialIndentLevel = 0;
rangeOffset = 0;
}
let eol = getEOL(document, lines);
let eol = getEOL(document);
let lineBreak = false;
let indentLevel = 0;
@ -59,7 +58,7 @@ export function format(document: ITextDocument, lines: LinesModel, range: Range,
let editOperations: TextEdit[] = [];
function addEdit(text: string, startOffset: number, endOffset: number) {
if (documentText.substring(startOffset, endOffset) !== text) {
let replaceRange = Range.create(lines.positionAt(startOffset), lines.positionAt(endOffset));
let replaceRange = Range.create(document.positionAt(startOffset), document.positionAt(endOffset));
editOperations.push(TextEdit.replace(replaceRange, text));
}
}
@ -162,10 +161,10 @@ function computeIndentLevel(content: string, offset: number, options: Formatting
return Math.floor(nChars / tabSize);
}
function getEOL(document: ITextDocument, lines: LinesModel): string {
function getEOL(document: ITextDocument): string {
let text = document.getText();
if (lines.lineCount > 1) {
let to = lines.offsetAt(Position.create(1, 0));
if (document.lineCount > 1) {
let to = document.offsetAt(Position.create(1, 0));
let from = to;
while (from > 0 && isEOL(text, from - 1)) {
from--;

View file

@ -10,8 +10,6 @@ import SchemaService = require('./jsonSchemaService');
import {Hover, ITextDocument, TextDocumentPosition, Range, MarkedString, RemoteConsole} from 'vscode-languageserver';
import {LinesModel} from './utils/lines';
export class JSONHover {
private schemaService: SchemaService.IJSONSchemaService;
@ -20,9 +18,9 @@ export class JSONHover {
this.schemaService = schemaService;
}
public doHover(document: ITextDocument, textDocumentPosition: TextDocumentPosition, lines: LinesModel, doc: Parser.JSONDocument): Thenable<Hover> {
public doHover(document: ITextDocument, textDocumentPosition: TextDocumentPosition, doc: Parser.JSONDocument): Thenable<Hover> {
let offset = lines.offsetAt(textDocumentPosition.position);
let offset = document.offsetAt(textDocumentPosition.position);
let node = doc.getNodeFromOffset(offset);
let originalNode = node;
@ -56,7 +54,7 @@ export class JSONHover {
});
if (description) {
let range = Range.create(lines.positionAt(node.start), lines.positionAt(node.end));
let range = Range.create(document.positionAt(node.start), document.positionAt(node.end));
let result: Hover = {
contents: [description],
range: range

View file

@ -18,7 +18,6 @@ import path = require('path');
import fs = require('fs');
import URI from './utils/uri';
import Strings = require('./utils/strings');
import {create as createLinesModel, LinesModel} from './utils/lines';
import {IWorkspaceContextService, ITelemetryService, JSONSchemaService, ISchemaContributions, ISchemaAssociations} from './jsonSchemaService';
import {parse as parseJSON, ObjectASTNode, JSONDocument} from './jsonParser';
import {JSONCompletion} from './jsonCompletion';
@ -201,7 +200,6 @@ function validateTextDocument(textDocument: ITextDocument): void {
}
let diagnostics: Diagnostic[] = [];
let lineModel = getLinesModel(textDocument);
let added: { [signature: string]: boolean } = {};
jsonDocument.errors.concat(jsonDocument.warnings).forEach((error, idx) => {
// remove duplicated messages
@ -209,8 +207,8 @@ function validateTextDocument(textDocument: ITextDocument): void {
if (!added[signature]) {
added[signature] = true;
let range = {
start: lineModel.positionAt(error.location.start),
end: lineModel.positionAt(error.location.end)
start: textDocument.positionAt(error.location.start),
end: textDocument.positionAt(error.location.end)
};
diagnostics.push({
severity: idx >= jsonDocument.errors.length ? DiagnosticSeverity.Warning : DiagnosticSeverity.Error,
@ -237,46 +235,36 @@ connection.onDidChangeWatchedFiles((change) => {
}
});
function getLinesModel(document: ITextDocument): LinesModel {
return createLinesModel(document.getText());
}
function getJSONDocument(document: ITextDocument): JSONDocument {
return parseJSON(document.getText());
}
connection.onCompletion((textDocumentPosition: TextDocumentPosition): Thenable<CompletionItem[]> => {
let document = documents.get(textDocumentPosition.uri);
let lines = getLinesModel(document);
let jsonDocument = getJSONDocument(document);
return jsonCompletion.doSuggest(document, textDocumentPosition, lines, jsonDocument);
return jsonCompletion.doSuggest(document, textDocumentPosition, jsonDocument);
});
connection.onHover((textDocumentPosition: TextDocumentPosition): Thenable<Hover> => {
let document = documents.get(textDocumentPosition.uri);
let lines = getLinesModel(document);
let jsonDocument = getJSONDocument(document);
return jsonHover.doHover(document, textDocumentPosition, lines, jsonDocument);
return jsonHover.doHover(document, textDocumentPosition, jsonDocument);
});
connection.onDocumentSymbol((textDocumentIdentifier: TextDocumentIdentifier): Thenable<SymbolInformation[]> => {
let document = documents.get(textDocumentIdentifier.uri);
let lines = getLinesModel(document);
let jsonDocument = getJSONDocument(document);
return jsonDocumentSymbols.compute(document, lines, jsonDocument);
return jsonDocumentSymbols.compute(document, jsonDocument);
});
connection.onDocumentFormatting((formatParams: DocumentFormattingParams) => {
let document = documents.get(formatParams.textDocument.uri);
let lines = getLinesModel(document);
return formatJSON(document, lines, null, formatParams.options);
return formatJSON(document, null, formatParams.options);
});
connection.onDocumentRangeFormatting((formatParams: DocumentRangeFormattingParams) => {
let document = documents.get(formatParams.textDocument.uri);
let lines = getLinesModel(document);
return formatJSON(document, lines, formatParams.range, formatParams.options);
return formatJSON(document, formatParams.range, formatParams.options);
});
// Listen on the connection

View file

@ -10,7 +10,6 @@ import SchemaService = require('../jsonSchemaService');
import JsonSchema = require('../json-toolbox/jsonSchema');
import {JSONCompletion} from '../jsonCompletion';
import {IXHROptions, IXHRResponse} from '../utils/httpRequest';
import {create as createLinesModel} from '../utils/lines';
import {CompletionItem, CompletionItemKind, CompletionOptions, ITextDocument, TextDocumentIdentifier, TextDocumentPosition, Range, Position, TextEdit} from 'vscode-languageserver';
@ -40,9 +39,8 @@ suite('JSON Completion', () => {
var document = ITextDocument.create(uri, value);
var textDocumentLocation = TextDocumentPosition.create(uri, Position.create(0, idx));
var lines = createLinesModel(value);
var jsonDoc = Parser.parse(value);
return completionProvider.doSuggest(document, textDocumentLocation, lines, jsonDoc);
return completionProvider.doSuggest(document, textDocumentLocation, jsonDoc);
};

View file

@ -10,7 +10,6 @@ import SchemaService = require('../jsonSchemaService');
import JsonSchema = require('../json-toolbox/jsonSchema');
import {JSONCompletion} from '../jsonCompletion';
import {IXHROptions, IXHRResponse} from '../utils/httpRequest';
import {create as createLinesModel} from '../utils/lines';
import {JSONDocumentSymbols} from '../jsonDocumentSymbols';
import {SymbolInformation, SymbolKind, TextDocumentIdentifier, ITextDocument, TextDocumentPosition, Range, Position, TextEdit} from 'vscode-languageserver';
@ -23,9 +22,8 @@ suite('JSON Document Symbols', () => {
var symbolProvider = new JSONDocumentSymbols();
var document = ITextDocument.create(uri, value);
var lines = createLinesModel(value);
var jsonDoc = Parser.parse(value);
return symbolProvider.compute(document, lines, jsonDoc);
return symbolProvider.compute(document, jsonDoc);
}
var assertOutline: any = function(actual: SymbolInformation[], expected: any[], message: string) {

View file

@ -5,10 +5,7 @@
'use strict';
import Json = require('../json-toolbox/json');
import {
ITextDocument, DocumentFormattingParams, Range, Position, FormattingOptions, TextEdit
} from 'vscode-languageserver';
import {LinesModel, create as createLinesModel} from '../utils/lines';
import {ITextDocument, DocumentFormattingParams, Range, Position, FormattingOptions, TextEdit} from 'vscode-languageserver';
import Formatter = require('../jsonFormatter');
import assert = require('assert');
@ -18,29 +15,26 @@ suite('JSON Formatter', () => {
let range: Range = null;
let uri = 'test://test.json';
let lines = createLinesModel(unformatted);
let rangeStart = unformatted.indexOf('|');
let rangeEnd = unformatted.lastIndexOf('|');
if (rangeStart !== -1 && rangeEnd !== -1) {
// remove '|'
var unformattedDoc = ITextDocument.create(uri, unformatted);
unformatted = unformatted.substring(0, rangeStart) + unformatted.substring(rangeStart + 1, rangeEnd) + unformatted.substring(rangeEnd + 1);
let startPos = lines.positionAt(rangeStart);
let endPos = lines.positionAt(rangeEnd);
let startPos = unformattedDoc.positionAt(rangeStart);
let endPos = unformattedDoc.positionAt(rangeEnd);
range = Range.create(startPos, endPos);
lines = createLinesModel(unformatted);
}
var document = ITextDocument.create(uri, unformatted);
let edits = Formatter.format(document, lines, range, { tabSize: 2, insertSpaces: insertSpaces });
let edits = Formatter.format(document, range, { tabSize: 2, insertSpaces: insertSpaces });
let formatted = unformatted;
let sortedEdits = edits.sort((a, b) => lines.offsetAt(b.range.start) - lines.offsetAt(a.range.start));
let sortedEdits = edits.sort((a, b) => document.offsetAt(b.range.start) - document.offsetAt(a.range.start));
let lastOffset = formatted.length;
sortedEdits.forEach(e => {
let startOffset = lines.offsetAt(e.range.start);
let endOffset = lines.offsetAt(e.range.end);
let startOffset = document.offsetAt(e.range.start);
let endOffset = document.offsetAt(e.range.end);
assert.ok(startOffset <= endOffset);
assert.ok(endOffset <= lastOffset);
formatted = formatted.substring(0, startOffset) + e.newText + formatted.substring(endOffset, formatted.length);

View file

@ -10,7 +10,6 @@ import SchemaService = require('../jsonSchemaService');
import JsonSchema = require('../json-toolbox/jsonSchema');
import {JSONCompletion} from '../jsonCompletion';
import {IXHROptions, IXHRResponse} from '../utils/httpRequest';
import {create as createLinesModel} from '../utils/lines';
import {JSONHover} from '../jsonHover';
import {Hover, ITextDocument, TextDocumentIdentifier, TextDocumentPosition, Range, Position, TextEdit} from 'vscode-languageserver';
@ -27,9 +26,8 @@ suite('JSON Hover', () => {
var document = ITextDocument.create(uri, value);
var textDocumentLocation = TextDocumentPosition.create(uri, position);
var lines = createLinesModel(value);
var jsonDoc = Parser.parse(value);
return hoverProvider.doHover(document, textDocumentLocation, lines, jsonDoc);
return hoverProvider.doHover(document, textDocumentLocation, jsonDoc);
}
var requestService = function(options: IXHROptions): Promise<IXHRResponse> {

View file

@ -1,81 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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 assert = require('assert');
import lines = require('../utils/lines');
import {Position} from 'vscode-languageserver';
suite('Lines Model Validator', () => {
test('single line', () => {
var str = "Hello World";
var lm = lines.create(str);
assert.equal(lm.lineCount, 1);
for (var i = 0; i < str.length; i++) {
assert.equal(lm.offsetAt(Position.create(0, i)), i);
assert.deepEqual(lm.positionAt(i), Position.create(0, i));
}
});
test('Mutiple lines', () => {
var str = "ABCDE\nFGHIJ\nKLMNO\n";
var lm = lines.create(str);
assert.equal(lm.lineCount, 4);
for (var i = 0; i < str.length; i++) {
var line = Math.floor(i / 6);
var column = i % 6;
assert.equal(lm.offsetAt(Position.create(line, column)), i);
assert.deepEqual(lm.positionAt(i), Position.create(line, column));
}
assert.equal(lm.offsetAt(Position.create(3, 0)), 18);
assert.equal(lm.offsetAt(Position.create(3, 1)), 18);
assert.deepEqual(lm.positionAt(18), Position.create(3, 0));
assert.deepEqual(lm.positionAt(19), Position.create(3, 0));
});
test('New line characters', () => {
var str = "ABCDE\rFGHIJ";
assert.equal(lines.create(str).lineCount, 2);
var str = "ABCDE\nFGHIJ";
assert.equal(lines.create(str).lineCount, 2);
var str = "ABCDE\r\nFGHIJ";
assert.equal(lines.create(str).lineCount, 2);
str = "ABCDE\n\nFGHIJ";
assert.equal(lines.create(str).lineCount, 3);
str = "ABCDE\r\rFGHIJ";
assert.equal(lines.create(str).lineCount, 3);
str = "ABCDE\n\rFGHIJ";
assert.equal(lines.create(str).lineCount, 3);
})
test('invalid inputs', () => {
var str = "Hello World";
var lm = lines.create(str);
// invalid position
assert.equal(lm.offsetAt(Position.create(0, str.length)), str.length);
assert.equal(lm.offsetAt(Position.create(0, str.length + 3)), str.length);
assert.equal(lm.offsetAt(Position.create(2, 3)), str.length);
assert.equal(lm.offsetAt(Position.create(-1, 3)), 0);
assert.equal(lm.offsetAt(Position.create(0, -3)), 0);
assert.equal(lm.offsetAt(Position.create(1, -3)), str.length);
// invalid offsets
assert.deepEqual(lm.positionAt(-1), Position.create(0, 0));
assert.deepEqual(lm.positionAt(str.length), Position.create(0, str.length));
assert.deepEqual(lm.positionAt(str.length + 3), Position.create(0, str.length));
});
});

View file

@ -1,88 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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 {
Position
} from 'vscode-languageserver';
export interface LinesModel {
/**
* Converts a zero-based offset to a position.
*
* @param offset A zero-based offset.
* @return A valid [position](#Position).
*/
positionAt(offset: number): Position;
/**
* Converts the position to a zero-based offset.
*
* The position will be [adjusted](#TextDocument.validatePosition).
*
* @param position A position.
* @return A valid zero-based offset.
*/
offsetAt(position: Position): number;
/**
* The number of lines in this document.
*
* @readonly
*/
lineCount: number;
}
export function create(text:string) : LinesModel {
const lineStarts: number[] = [];
var isLineStart = true;
for (let i = 0; i < text.length; i++) {
if (isLineStart) {
lineStarts.push(i);
isLineStart = false;
}
var ch = text.charAt(i);
isLineStart = (ch === '\r' || ch === '\n');
if (ch === '\r' && i + 1 < text.length && text.charAt(i+1) === '\n') {
i++;
}
}
if (isLineStart && text.length > 0) {
lineStarts.push(text.length);
}
return {
positionAt: (offset:number) => {
offset = Math.max(Math.min(offset, text.length), 0);
let low = 0, high = lineStarts.length;
if (high === 0) {
return Position.create(0, offset);
}
while (low < high) {
let mid = Math.floor((low + high) / 2);
if (lineStarts[mid] > offset) {
high = mid;
} else {
low = mid + 1;
}
}
// low is the least x for which the line offset is larger than the offset
// or array.length if no element fullfills the given function.
var line = low - 1;
return Position.create(line, offset - lineStarts[line]);
},
offsetAt: (position: Position) => {
if (position.line >= lineStarts.length) {
return text.length;
} else if (position.line < 0) {
return 0;
}
var lineStart = lineStarts[position.line];
var nextLineStart = (position.line + 1 < lineStarts.length) ? lineStarts[position.line + 1] : text.length;
return Math.max(Math.min(lineStart + position.character, nextLineStart), lineStart);
},
lineCount: lineStarts.length
}
}