Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

An additional parameter keepLines has been added into the formatting options which allows to keep the original line formatting #66

Merged
merged 9 commits into from
Jul 11, 2022
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
3.1.0 2022-07-07
==================
* Added an an additional feature `keepLines` in the formatting of JSON documents which leaves the lines as is
aiday-mar marked this conversation as resolved.
Show resolved Hide resolved

3.0.0 2020-11-13
==================
* fixed API spec for `parseTree`. Can return `undefine` for empty input.
Expand Down
151 changes: 115 additions & 36 deletions src/impl/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';

import { mainModule } from 'process';
aiday-mar marked this conversation as resolved.
Show resolved Hide resolved
import { Range, FormattingOptions, Edit, SyntaxKind, ScanError } from '../main';
import { createScanner } from './scanner';

Expand Down Expand Up @@ -36,7 +37,7 @@ export function format(documentText: string, range: Range | undefined, options:
}
let eol = getEOL(options, documentText);

let lineBreak = false;
let numberLineBreaks = 0;
let indentLevel = 0;
let indentValue: string;
if (options.insertSpaces) {
Expand All @@ -48,14 +49,23 @@ export function format(documentText: string, range: Range | undefined, options:
let scanner = createScanner(formatText, false);
let hasError = false;

function newLineAndIndent(): string {
return eol + repeat(indentValue, initialIndentLevel + indentLevel);
function newLinesAndIndent(): string {
if (numberLineBreaks > 1) {
return repeat(eol, numberLineBreaks) + repeat(indentValue, initialIndentLevel + indentLevel);
} else {
return eol + repeat(indentValue, initialIndentLevel + indentLevel);
}
}

function scanNext(): SyntaxKind {
let token = scanner.scan();
lineBreak = false;
numberLineBreaks = 0;
while (token === SyntaxKind.Trivia || token === SyntaxKind.LineBreakTrivia) {
lineBreak = lineBreak || (token === SyntaxKind.LineBreakTrivia);
if (token === SyntaxKind.LineBreakTrivia && options.keepLines) {
numberLineBreaks += 1;
} else if (token === SyntaxKind.LineBreakTrivia) {
numberLineBreaks = 1;
}
token = scanner.scan();
}
hasError = token === SyntaxKind.Unknown || scanner.getTokenError() !== ScanError.None;
Expand All @@ -79,85 +89,154 @@ export function format(documentText: string, range: Range | undefined, options:
while (firstToken !== SyntaxKind.EOF) {
let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
let secondToken = scanNext();

let replaceContent = '';
let needsLineBreak = false;
while (!lineBreak && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) {
// comments on the same line: keep them on the same line, but ignore them otherwise
while (numberLineBreaks === 0 && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) {
let commentTokenStart = scanner.getTokenOffset() + formatTextStart;
addEdit(' ', firstTokenEnd, commentTokenStart);
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
needsLineBreak = secondToken === SyntaxKind.LineCommentTrivia;
replaceContent = needsLineBreak ? newLineAndIndent() : '';
replaceContent = needsLineBreak ? newLinesAndIndent() : '';
secondToken = scanNext();
}

if (secondToken === SyntaxKind.CloseBraceToken) {
if (firstToken !== SyntaxKind.OpenBraceToken) {
indentLevel--;
replaceContent = newLineAndIndent();
if (firstToken !== SyntaxKind.OpenBraceToken) { indentLevel--; };
if (options.keepLines) {
if (numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
} else {
replaceContent = ' ';
}
} else if (firstToken !== SyntaxKind.OpenBraceToken) {
replaceContent = newLinesAndIndent();
}
} else if (secondToken === SyntaxKind.CloseBracketToken) {
if (firstToken !== SyntaxKind.OpenBracketToken) {
indentLevel--;
replaceContent = newLineAndIndent();
if (firstToken !== SyntaxKind.OpenBracketToken) { indentLevel--; };
if (options.keepLines) {
if (numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
} else {
replaceContent = ' ';
}
}
else if (firstToken !== SyntaxKind.OpenBracketToken) {
replaceContent = newLinesAndIndent();
}
} else {
switch (firstToken) {
case SyntaxKind.OpenBracketToken:
case SyntaxKind.OpenBraceToken:
indentLevel++;
replaceContent = newLineAndIndent();
if (options.keepLines) {
if (numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
} else {
replaceContent = " ";
}
} else {
replaceContent = newLinesAndIndent();
}
break;
case SyntaxKind.CommaToken:
if (options.keepLines) {
if (numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
} else {
replaceContent = " ";
}
} else {
replaceContent = newLinesAndIndent();
}
break;
case SyntaxKind.LineCommentTrivia:
replaceContent = newLineAndIndent();
replaceContent = newLinesAndIndent();
break;
case SyntaxKind.BlockCommentTrivia:
if (lineBreak) {
replaceContent = newLineAndIndent();
if (numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
} else if (!needsLineBreak) {
// symbol following comment on the same line: keep on same line, separate with ' '
replaceContent = ' ';
}
break;
case SyntaxKind.ColonToken:
if (!needsLineBreak) {
replaceContent = ' ';
if (options.keepLines) {
aiday-mar marked this conversation as resolved.
Show resolved Hide resolved
if (numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
} else if (!needsLineBreak) {
replaceContent = ' ';
}
} else {
if (!needsLineBreak) {
replaceContent = ' ';
}
}
break;
case SyntaxKind.StringLiteral:
if (secondToken === SyntaxKind.ColonToken) {
if (!needsLineBreak) {
replaceContent = '';
if (options.keepLines) {
if (numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
} else {
if (secondToken === SyntaxKind.ColonToken) {
if (!needsLineBreak) {
replaceContent = '';
}
}
}
} else {
if (secondToken === SyntaxKind.ColonToken) {
if (!needsLineBreak) {
replaceContent = '';
}
}
break;
}
// fall through
break;
case SyntaxKind.NullKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.NumericLiteral:
case SyntaxKind.CloseBraceToken:
case SyntaxKind.CloseBracketToken:
if (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia) {
if (!needsLineBreak) {
replaceContent = ' ';
if (options.keepLines) {
if (numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
} else {
if (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia) {
if (!needsLineBreak) {
replaceContent = ' ';
}
} else if (secondToken !== SyntaxKind.CommaToken && secondToken !== SyntaxKind.EOF) {
hasError = true;
}
}
} else {
if (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia) {
if (!needsLineBreak) {
replaceContent = ' ';
}
} else if (secondToken !== SyntaxKind.CommaToken && secondToken !== SyntaxKind.EOF) {
hasError = true;
}
} else if (secondToken !== SyntaxKind.CommaToken && secondToken !== SyntaxKind.EOF) {
hasError = true;
}
break;
case SyntaxKind.Unknown:
hasError = true;
break;
}
if (lineBreak && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) {
replaceContent = newLineAndIndent();
if (numberLineBreaks > 0 && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) {
replaceContent = newLinesAndIndent();
}
}
if (secondToken === SyntaxKind.EOF) {
replaceContent = options.insertFinalNewline ? eol : '';
if (options.keepLines) {
if (numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
} else {
replaceContent = options.insertFinalNewline ? eol : '';
}
} else {
replaceContent = options.insertFinalNewline ? eol : '';
}
}
let secondTokenStart = scanner.getTokenOffset() + formatTextStart;
addEdit(replaceContent, firstTokenEnd, secondTokenStart);
Expand Down Expand Up @@ -209,4 +288,4 @@ function getEOL(options: FormattingOptions, text: string): string {

export function isEOL(text: string, offset: number) {
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
}
}
4 changes: 4 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,10 @@ export interface FormattingOptions {
* If set, will add a new line at the end of the document.
*/
insertFinalNewline?: boolean;
/**
* If true, will keep line positions as is in the formatting
*/
keepLines?: boolean;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/test/edit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ suite('JSON - edits', () => {
let formattingOptions: FormattingOptions = {
insertSpaces: true,
tabSize: 2,
eol: '\n'
eol: '\n',
keepLines: false
};

let options: ModificationOptions = {
Expand Down
Loading