Skip to content
This repository has been archived by the owner on Jun 15, 2021. It is now read-only.

feat: match formatting indentation to terraform fmt #86

Merged
merged 1 commit into from
May 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@
"request": "launch",
"name": "Jest Current File",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": ["${relativeFile}"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"args": ["${fileBasename}"],
"outFiles": ["${workspaceFolder}/out/**"],
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
Expand Down
9 changes: 9 additions & 0 deletions docs/formatting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Formatting Guidelines

Following the ones used by `terraform fmt`:

> https://www.terraform.io/docs/configuration/style.html

- Indent two spaces for each nesting level.
- Inside a block, arguments go together first, then blocks (separated by a blank line each).
- Align their equal sign according to the longest key.
2 changes: 2 additions & 0 deletions gulpfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { BuildTasks } from "./scripts/gulp-build";
import { LinterTasks } from "./scripts/gulp-linter";
import { VSCodeTasks } from "./scripts/gulp-vscode";

// TODO: replace all module-like files with classes

// Called by debugger before launching
gulp.task("update-vscode", gulp.series([BuildTasks.compile, VSCodeTasks.copyFiles, VSCodeTasks.generatePackageJson]));

Expand Down
4 changes: 2 additions & 2 deletions src/binding/bound-nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
ObjectMemberSyntax,
BasePropertySyntax,
} from "../parsing/syntax-nodes";
import { Token } from "../scanning/tokens";
import { TokenWithTrivia } from "../scanning/tokens";

export enum BoundKind {
// Top level
Expand Down Expand Up @@ -147,7 +147,7 @@ export class BoundSecrets extends BaseBoundNode {
}

export class BoundStringValue extends BaseBoundNode {
public constructor(public readonly value: string, public readonly syntax: Token) {
public constructor(public readonly value: string, public readonly syntax: TokenWithTrivia) {
super(BoundKind.StringValue);
}
}
Expand Down
74 changes: 63 additions & 11 deletions src/parsing/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

import { DiagnosticBag } from "../util/diagnostics";
import { Token, TokenKind, getTokenDescription } from "../scanning/tokens";
import { Token, TokenKind, getTokenDescription, TokenWithTrivia } from "../scanning/tokens";
import {
DocumentSyntax,
VersionSyntax,
Expand All @@ -22,7 +22,9 @@ interface ParseContext {
}

export function parseTokens(allTokens: ReadonlyArray<Token>, bag: DiagnosticBag): DocumentSyntax {
const tokens = allTokens.filter(token => token.kind !== TokenKind.Comment && token.kind !== TokenKind.Unrecognized);
const tokens = allTokens.filter(token => token.kind !== TokenKind.Unrecognized);
const commentsAfter = extractCommentsAtEndOfFile();

const reportedErrors = Array<boolean>();

let index = 0;
Expand All @@ -35,7 +37,18 @@ export function parseTokens(allTokens: ReadonlyArray<Token>, bag: DiagnosticBag)
});
}

return new DocumentSyntax(versions, blocks);
return new DocumentSyntax(versions, blocks, commentsAfter);

function extractCommentsAtEndOfFile(): ReadonlyArray<TokenWithTrivia> {
let end = tokens.length - 1;
while (end >= 0 && tokens[end].kind === TokenKind.Comment) {
end -= 1;
}

const result = tokens.slice(end + 1);
tokens.splice(end + 1);
return result;
}

function parseTopLevelNode(context: ParseContext): void {
const keywordKinds = [TokenKind.VersionKeyword, TokenKind.WorkflowKeyword, TokenKind.ActionKeyword];
Expand Down Expand Up @@ -66,14 +79,14 @@ export function parseTokens(allTokens: ReadonlyArray<Token>, bag: DiagnosticBag)
}
}

function parseVersion(version: Token, context: ParseContext): void {
function parseVersion(version: TokenWithTrivia, context: ParseContext): void {
const equal = eat(context, TokenKind.Equal);
const integer = eat(context, TokenKind.IntegerLiteral);

versions.push(new VersionSyntax(version, equal, integer));
}

function parseBlock(type: Token, context: ParseContext): void {
function parseBlock(type: TokenWithTrivia, context: ParseContext): void {
const name = eat(context, TokenKind.StringLiteral);
const openBracket = eat(context, TokenKind.LeftCurlyBracket);

Expand Down Expand Up @@ -118,7 +131,7 @@ export function parseTokens(allTokens: ReadonlyArray<Token>, bag: DiagnosticBag)
return properties;
}

function parseProperty(key: Token, context: ParseContext): BasePropertySyntax {
function parseProperty(key: TokenWithTrivia, context: ParseContext): BasePropertySyntax {
const equal = eat(context, TokenKind.Equal);
const valueStart = eat(context, TokenKind.StringLiteral, TokenKind.LeftCurlyBracket, TokenKind.LeftSquareBracket);

Expand Down Expand Up @@ -163,7 +176,7 @@ export function parseTokens(allTokens: ReadonlyArray<Token>, bag: DiagnosticBag)
break;
}

let comma: Token | undefined;
let comma: TokenWithTrivia | undefined;
if (isNext(TokenKind.Comma)) {
comma = eat(context, TokenKind.Comma);
}
Expand Down Expand Up @@ -197,16 +210,37 @@ export function parseTokens(allTokens: ReadonlyArray<Token>, bag: DiagnosticBag)
return index < tokens.length && tokens[index].kind === kind;
}

function eat(context: ParseContext, ...expected: TokenKind[]): Token {
function eat(context: ParseContext, ...expected: TokenKind[]): TokenWithTrivia {
const commentsBefore = eatComments();

while (true) {
if (index >= tokens.length) {
return missingToken(expected);
return {
commentsBefore,
...missingToken(expected),
};
}

const current = tokens[index];
if (expected.includes(current.kind)) {
index += 1;
return current;

if (index < tokens.length) {
const commentAfter = tokens[index];
if (commentAfter.kind === TokenKind.Comment && commentAfter.range.start.line === current.range.end.line) {
index += 1;
return {
commentsBefore,
...current,
commentAfter,
};
}
}

return {
commentsBefore,
...current,
};
}

let canBeHandledByParent = false;
Expand All @@ -217,7 +251,10 @@ export function parseTokens(allTokens: ReadonlyArray<Token>, bag: DiagnosticBag)
}

if (canBeHandledByParent) {
return missingToken(expected);
return {
commentsBefore,
...missingToken(expected),
};
}

if (!reportedErrors[index]) {
Expand All @@ -229,6 +266,21 @@ export function parseTokens(allTokens: ReadonlyArray<Token>, bag: DiagnosticBag)
}
}

function eatComments(): ReadonlyArray<TokenWithTrivia> | undefined {
let result: TokenWithTrivia[] | undefined;

while (index < tokens.length && tokens[index].kind === TokenKind.Comment) {
if (!result) {
result = [];
}

result.push(tokens[index]);
index += 1;
}

return result;
}

function missingToken(expected: TokenKind[]): Token {
let missingIndex = index;
const endOfFile = index >= tokens.length;
Expand Down
67 changes: 40 additions & 27 deletions src/parsing/syntax-nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright 2019 Omar Tawfik. Please see LICENSE file at the root of this repository.
*/

import { Token, TokenKind, getTokenDescription } from "../scanning/tokens";
import { Token, TokenKind, getTokenDescription, TokenWithTrivia } from "../scanning/tokens";
import { Range } from "vscode-languageserver-types";
import { comparePositions } from "../util/ranges";

Expand Down Expand Up @@ -59,35 +59,40 @@ export class DocumentSyntax extends BaseSyntaxNode {
public constructor(
public readonly versions: ReadonlyArray<VersionSyntax>,
public readonly blocks: ReadonlyArray<BlockSyntax>,
public readonly commentsAfter: ReadonlyArray<TokenWithTrivia>,
) {
super(SyntaxKind.Document);
}

public calculateRange(): Range {
return combineRange(...this.versions, ...this.blocks);
protected calculateRange(): Range {
return combineRange(...this.versions, ...this.blocks, ...this.commentsAfter);
}
}

export class VersionSyntax extends BaseSyntaxNode {
public constructor(public readonly version: Token, public readonly equal: Token, public readonly integer: Token) {
public constructor(
public readonly version: TokenWithTrivia,
public readonly equal: TokenWithTrivia,
public readonly integer: TokenWithTrivia,
) {
super(SyntaxKind.Version);
assertTokenKind(version, TokenKind.VersionKeyword);
assertTokenKind(equal, TokenKind.Equal);
assertTokenKind(integer, TokenKind.IntegerLiteral);
}

public calculateRange(): Range {
protected calculateRange(): Range {
return combineRange(this.version, this.equal, this.integer);
}
}

export class BlockSyntax extends BaseSyntaxNode {
public constructor(
public readonly type: Token,
public readonly name: Token,
public readonly openBracket: Token,
public readonly type: TokenWithTrivia,
public readonly name: TokenWithTrivia,
public readonly openBracket: TokenWithTrivia,
public readonly properties: ReadonlyArray<BasePropertySyntax>,
public readonly closeBracket: Token,
public readonly closeBracket: TokenWithTrivia,
) {
super(SyntaxKind.Block);
assertTokenKind(type, TokenKind.ActionKeyword, TokenKind.WorkflowKeyword);
Expand All @@ -96,13 +101,17 @@ export class BlockSyntax extends BaseSyntaxNode {
assertTokenKind(closeBracket, TokenKind.RightCurlyBracket);
}

public calculateRange(): Range {
protected calculateRange(): Range {
return combineRange(this.type, this.name, this.openBracket, ...this.properties, this.closeBracket);
}
}

export abstract class BasePropertySyntax extends BaseSyntaxNode {
protected constructor(kind: SyntaxKind, public readonly key: Token, public readonly equal: Token) {
protected constructor(
kind: SyntaxKind,
public readonly key: TokenWithTrivia,
public readonly equal: TokenWithTrivia,
) {
super(kind);
assertTokenKind(
key,
Expand All @@ -120,73 +129,77 @@ export abstract class BasePropertySyntax extends BaseSyntaxNode {
}

export class StringPropertySyntax extends BasePropertySyntax {
public constructor(key: Token, equal: Token, public readonly value: Token | undefined) {
public constructor(key: TokenWithTrivia, equal: TokenWithTrivia, public readonly value: TokenWithTrivia | undefined) {
super(SyntaxKind.StringProperty, key, equal);
assertTokenKind(value, TokenKind.StringLiteral);
}

public calculateRange(): Range {
protected calculateRange(): Range {
return combineRange(this.key, this.equal, this.value);
}
}

export class ArrayPropertySyntax extends BasePropertySyntax {
public constructor(
key: Token,
equal: Token,
public readonly openBracket: Token,
key: TokenWithTrivia,
equal: TokenWithTrivia,
public readonly openBracket: TokenWithTrivia,
public readonly items: ReadonlyArray<ArrayItemSyntax>,
public readonly closeBracket: Token,
public readonly closeBracket: TokenWithTrivia,
) {
super(SyntaxKind.ArrayProperty, key, equal);
assertTokenKind(openBracket, TokenKind.LeftSquareBracket);
assertTokenKind(closeBracket, TokenKind.RightSquareBracket);
}

public calculateRange(): Range {
protected calculateRange(): Range {
return combineRange(this.openBracket, ...this.items, this.closeBracket);
}
}

export class ArrayItemSyntax extends BaseSyntaxNode {
public constructor(public readonly value: Token, public readonly comma: Token | undefined) {
public constructor(public readonly value: TokenWithTrivia, public readonly comma: TokenWithTrivia | undefined) {
super(SyntaxKind.ArrayItem);
assertTokenKind(value, TokenKind.StringLiteral);
assertTokenKind(comma, TokenKind.Comma);
}

public calculateRange(): Range {
protected calculateRange(): Range {
return combineRange(this.value, this.comma);
}
}

export class ObjectPropertySyntax extends BasePropertySyntax {
public constructor(
key: Token,
equal: Token,
public readonly openBracket: Token,
key: TokenWithTrivia,
equal: TokenWithTrivia,
public readonly openBracket: TokenWithTrivia,
public readonly members: ReadonlyArray<ObjectMemberSyntax>,
public readonly closeBracket: Token,
public readonly closeBracket: TokenWithTrivia,
) {
super(SyntaxKind.ObjectProperty, key, equal);
assertTokenKind(openBracket, TokenKind.LeftCurlyBracket);
assertTokenKind(closeBracket, TokenKind.RightCurlyBracket);
}

public calculateRange(): Range {
protected calculateRange(): Range {
return combineRange(this.openBracket, ...this.members, this.closeBracket);
}
}

export class ObjectMemberSyntax extends BaseSyntaxNode {
public constructor(public readonly name: Token, public readonly equal: Token, public readonly value: Token) {
public constructor(
public readonly name: TokenWithTrivia,
public readonly equal: TokenWithTrivia,
public readonly value: TokenWithTrivia,
) {
super(SyntaxKind.ObjectMember);
assertTokenKind(name, TokenKind.Identifier);
assertTokenKind(equal, TokenKind.Equal);
assertTokenKind(value, TokenKind.StringLiteral);
}

public calculateRange(): Range {
protected calculateRange(): Range {
return combineRange(this.name, this.equal, this.value);
}
}
5 changes: 5 additions & 0 deletions src/scanning/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,8 @@ export interface Token {
readonly range: Range;
readonly text: string;
}

export interface TokenWithTrivia extends Token {
readonly commentsBefore?: ReadonlyArray<TokenWithTrivia>;
readonly commentAfter?: TokenWithTrivia;
}
Loading