--- /dev/null
+/*---------------------------------------------------------------------------------------------
+ * 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 { createScanner } from './scanner';
+export function format(documentText, range, options) {
+ var initialIndentLevel;
+ var formatText;
+ var formatTextStart;
+ var rangeStart;
+ var rangeEnd;
+ if (range) {
+ rangeStart = range.offset;
+ rangeEnd = rangeStart + range.length;
+ formatTextStart = rangeStart;
+ while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) {
+ formatTextStart--;
+ }
+ var endOffset = rangeEnd;
+ while (endOffset < documentText.length && !isEOL(documentText, endOffset)) {
+ endOffset++;
+ }
+ formatText = documentText.substring(formatTextStart, endOffset);
+ initialIndentLevel = computeIndentLevel(formatText, options);
+ }
+ else {
+ formatText = documentText;
+ initialIndentLevel = 0;
+ formatTextStart = 0;
+ rangeStart = 0;
+ rangeEnd = documentText.length;
+ }
+ var eol = getEOL(options, documentText);
+ var lineBreak = false;
+ var indentLevel = 0;
+ var indentValue;
+ if (options.insertSpaces) {
+ indentValue = repeat(' ', options.tabSize || 4);
+ }
+ else {
+ indentValue = '\t';
+ }
+ var scanner = createScanner(formatText, false);
+ var hasError = false;
+ function newLineAndIndent() {
+ return eol + repeat(indentValue, initialIndentLevel + indentLevel);
+ }
+ function scanNext() {
+ var token = scanner.scan();
+ lineBreak = false;
+ while (token === 15 /* Trivia */ || token === 14 /* LineBreakTrivia */) {
+ lineBreak = lineBreak || (token === 14 /* LineBreakTrivia */);
+ token = scanner.scan();
+ }
+ hasError = token === 16 /* Unknown */ || scanner.getTokenError() !== 0 /* None */;
+ return token;
+ }
+ var editOperations = [];
+ function addEdit(text, startOffset, endOffset) {
+ if (!hasError && (!range || (startOffset < rangeEnd && endOffset > rangeStart)) && documentText.substring(startOffset, endOffset) !== text) {
+ editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text });
+ }
+ }
+ var firstToken = scanNext();
+ if (firstToken !== 17 /* EOF */) {
+ var firstTokenStart = scanner.getTokenOffset() + formatTextStart;
+ var initialIndent = repeat(indentValue, initialIndentLevel);
+ addEdit(initialIndent, formatTextStart, firstTokenStart);
+ }
+ while (firstToken !== 17 /* EOF */) {
+ var firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
+ var secondToken = scanNext();
+ var replaceContent = '';
+ var needsLineBreak = false;
+ while (!lineBreak && (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */)) {
+ // comments on the same line: keep them on the same line, but ignore them otherwise
+ var commentTokenStart = scanner.getTokenOffset() + formatTextStart;
+ addEdit(' ', firstTokenEnd, commentTokenStart);
+ firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
+ needsLineBreak = secondToken === 12 /* LineCommentTrivia */;
+ replaceContent = needsLineBreak ? newLineAndIndent() : '';
+ secondToken = scanNext();
+ }
+ if (secondToken === 2 /* CloseBraceToken */) {
+ if (firstToken !== 1 /* OpenBraceToken */) {
+ indentLevel--;
+ replaceContent = newLineAndIndent();
+ }
+ }
+ else if (secondToken === 4 /* CloseBracketToken */) {
+ if (firstToken !== 3 /* OpenBracketToken */) {
+ indentLevel--;
+ replaceContent = newLineAndIndent();
+ }
+ }
+ else {
+ switch (firstToken) {
+ case 3 /* OpenBracketToken */:
+ case 1 /* OpenBraceToken */:
+ indentLevel++;
+ replaceContent = newLineAndIndent();
+ break;
+ case 5 /* CommaToken */:
+ case 12 /* LineCommentTrivia */:
+ replaceContent = newLineAndIndent();
+ break;
+ case 13 /* BlockCommentTrivia */:
+ if (lineBreak) {
+ replaceContent = newLineAndIndent();
+ }
+ else if (!needsLineBreak) {
+ // symbol following comment on the same line: keep on same line, separate with ' '
+ replaceContent = ' ';
+ }
+ break;
+ case 6 /* ColonToken */:
+ if (!needsLineBreak) {
+ replaceContent = ' ';
+ }
+ break;
+ case 10 /* StringLiteral */:
+ if (secondToken === 6 /* ColonToken */) {
+ if (!needsLineBreak) {
+ replaceContent = '';
+ }
+ break;
+ }
+ // fall through
+ case 7 /* NullKeyword */:
+ case 8 /* TrueKeyword */:
+ case 9 /* FalseKeyword */:
+ case 11 /* NumericLiteral */:
+ case 2 /* CloseBraceToken */:
+ case 4 /* CloseBracketToken */:
+ if (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */) {
+ if (!needsLineBreak) {
+ replaceContent = ' ';
+ }
+ }
+ else if (secondToken !== 5 /* CommaToken */ && secondToken !== 17 /* EOF */) {
+ hasError = true;
+ }
+ break;
+ case 16 /* Unknown */:
+ hasError = true;
+ break;
+ }
+ if (lineBreak && (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */)) {
+ replaceContent = newLineAndIndent();
+ }
+ }
+ if (secondToken === 17 /* EOF */) {
+ replaceContent = options.insertFinalNewline ? eol : '';
+ }
+ var secondTokenStart = scanner.getTokenOffset() + formatTextStart;
+ addEdit(replaceContent, firstTokenEnd, secondTokenStart);
+ firstToken = secondToken;
+ }
+ return editOperations;
+}
+function repeat(s, count) {
+ var result = '';
+ for (var i = 0; i < count; i++) {
+ result += s;
+ }
+ return result;
+}
+function computeIndentLevel(content, options) {
+ var i = 0;
+ var nChars = 0;
+ var tabSize = options.tabSize || 4;
+ while (i < content.length) {
+ var ch = content.charAt(i);
+ if (ch === ' ') {
+ nChars++;
+ }
+ else if (ch === '\t') {
+ nChars += tabSize;
+ }
+ else {
+ break;
+ }
+ i++;
+ }
+ return Math.floor(nChars / tabSize);
+}
+function getEOL(options, text) {
+ for (var i = 0; i < text.length; i++) {
+ var ch = text.charAt(i);
+ if (ch === '\r') {
+ if (i + 1 < text.length && text.charAt(i + 1) === '\n') {
+ return '\r\n';
+ }
+ return '\r';
+ }
+ else if (ch === '\n') {
+ return '\n';
+ }
+ }
+ return (options && options.eol) || '\n';
+}
+export function isEOL(text, offset) {
+ return '\r\n'.indexOf(text.charAt(offset)) !== -1;
+}