diff --git a/extensions/json/server/src/jsonCompletion.ts b/extensions/json/server/src/jsonCompletion.ts index 39ae6588376..60b9832bc20 100644 --- a/extensions/json/server/src/jsonCompletion.ts +++ b/extensions/json/server/src/jsonCompletion.ts @@ -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 { + public doSuggest(document: ITextDocument, textDocumentPosition: TextDocumentPosition, doc: Parser.JSONDocument): Thenable { - 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 } = {}; diff --git a/extensions/json/server/src/jsonDocumentSymbols.ts b/extensions/json/server/src/jsonDocumentSymbols.ts index b62fa8c52f4..da5ca26c4b7 100644 --- a/extensions/json/server/src/jsonDocumentSymbols.ts +++ b/extensions/json/server/src/jsonDocumentSymbols.ts @@ -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 { + public compute(document: ITextDocument, doc: Parser.JSONDocument): Promise { let root = doc.root; if (!root) { @@ -33,7 +31,7 @@ export class JSONDocumentSymbols { if (item.type === 'object') { let property = (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 = 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; diff --git a/extensions/json/server/src/jsonFormatter.ts b/extensions/json/server/src/jsonFormatter.ts index 5d45068db61..9df8ed94784 100644 --- a/extensions/json/server/src/jsonFormatter.ts +++ b/extensions/json/server/src/jsonFormatter.ts @@ -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--; diff --git a/extensions/json/server/src/jsonHover.ts b/extensions/json/server/src/jsonHover.ts index a50976b1b4f..7395c471292 100644 --- a/extensions/json/server/src/jsonHover.ts +++ b/extensions/json/server/src/jsonHover.ts @@ -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 { + public doHover(document: ITextDocument, textDocumentPosition: TextDocumentPosition, doc: Parser.JSONDocument): Thenable { - 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 diff --git a/extensions/json/server/src/server.ts b/extensions/json/server/src/server.ts index 500bf338fa3..2ebb9d6f0f9 100644 --- a/extensions/json/server/src/server.ts +++ b/extensions/json/server/src/server.ts @@ -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 => { 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 => { 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 => { 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 diff --git a/extensions/json/server/src/test/completion.test.ts b/extensions/json/server/src/test/completion.test.ts index 49eeaa72c25..1c06db1853c 100644 --- a/extensions/json/server/src/test/completion.test.ts +++ b/extensions/json/server/src/test/completion.test.ts @@ -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); }; diff --git a/extensions/json/server/src/test/documentSymbols.test.ts b/extensions/json/server/src/test/documentSymbols.test.ts index a5b5526fc83..c852f25eefa 100644 --- a/extensions/json/server/src/test/documentSymbols.test.ts +++ b/extensions/json/server/src/test/documentSymbols.test.ts @@ -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) { diff --git a/extensions/json/server/src/test/formatter.test.ts b/extensions/json/server/src/test/formatter.test.ts index daea337a7a6..ccd94ad55bc 100644 --- a/extensions/json/server/src/test/formatter.test.ts +++ b/extensions/json/server/src/test/formatter.test.ts @@ -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); diff --git a/extensions/json/server/src/test/hover.test.ts b/extensions/json/server/src/test/hover.test.ts index 36d4d42ec9f..022fc17a06a 100644 --- a/extensions/json/server/src/test/hover.test.ts +++ b/extensions/json/server/src/test/hover.test.ts @@ -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 { diff --git a/extensions/json/server/src/test/lines.test.ts b/extensions/json/server/src/test/lines.test.ts deleted file mode 100644 index 3a4d8889563..00000000000 --- a/extensions/json/server/src/test/lines.test.ts +++ /dev/null @@ -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)); - }); -}); diff --git a/extensions/json/server/src/utils/lines.ts b/extensions/json/server/src/utils/lines.ts deleted file mode 100644 index 13d0c009087..00000000000 --- a/extensions/json/server/src/utils/lines.ts +++ /dev/null @@ -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 - } - -}