smart-column-indenter
Version:
A smart source code indenter that indent the code into columns
96 lines (79 loc) • 3.59 kB
text/typescript
import Language from "../Language";
import LineOfCode from '../LineOfCode';
import TypeScriptScanner, { SyntaxKind, nestableKinds } from "./TypeScriptScanner";
import TypeScriptToken, * as token from "./TypeScriptToken";
import * as tsc from "typescript";
import intersection = require('lodash.intersection');
export default class TypeScriptLanguage extends Language<TypeScriptToken>
{
protected readonly headOrTailMissingToken: string|undefined = ",";
private wordsIntersection: Set<string>;
public token2string(token: TypeScriptToken): string
{
if (nestableKinds.some(t => t[0] === token.syntaxKind || t[1] === token.syntaxKind)) {
return `${token.content}[${token.level}]`;
}
else if (SyntaxKind.FirstPunctuation <= token.syntaxKind && token.syntaxKind <= SyntaxKind.LastPunctuation) {
return token.content;
}
else if (SyntaxKind.FirstLiteralToken <= token.syntaxKind && token.syntaxKind <= SyntaxKind.LastLiteralToken) {
return token.kind;
}
else if (token.syntaxKind === SyntaxKind.Identifier) {
return this.wordsIntersection.has(token.content || "") ? `${token.kind}[${token.content}]` : token.kind;
}
else {
return token.content;
}
}
public stringify(lines: (TypeScriptToken[])[][]): string[]
{
const stringifiedLines = lines.map(line => "");
for (let column = 0; column < lines[0].length; column++) {
const lengths = lines.map(l => {
const tokens = l[column];
return tokens.map(t => t.content.length).reduce((a, b) => a + b, 0);
});
const maxLength = Math.max(...lengths);
for (let i = 0; i < lines.length; i++) {
const tokens = lines[i][column];
const content = tokens.map(t => t.content).join("");
stringifiedLines[i] += this.pad(content, maxLength);
}
}
return stringifiedLines;
}
public tokenize(linesOfCode: string[]): LineOfCode<TypeScriptToken>[]
{
const linesOfTokens = linesOfCode.map(line => {
const scanner = new TypeScriptScanner(line);
const tokens = [];
while(!scanner.endOfLine()) {
tokens.push(scanner.getToken());
}
return tokens;
});
const wordsByLine = linesOfTokens.map(line => line.filter(t => t.syntaxKind === SyntaxKind.Identifier).map(t => t.content));
this.wordsIntersection = new Set(intersection(...wordsByLine))
return linesOfTokens;
}
public preProcessInput(lines: LineOfCode<TypeScriptToken>[]): LineOfCode<TypeScriptToken>[]
{
let processedLines = lines;
processedLines = this.removeHeadOrTailMissingToken(processedLines, ",");
processedLines = processedLines.map(line => line.filter(t => t.syntaxKind !== SyntaxKind.WhitespaceTrivia));
return processedLines;
}
public preProcessOutput(lines: LineOfCode<TypeScriptToken>[]): LineOfCode<TypeScriptToken>[]
{
return lines.map(line => line.map(t => this.transformOutputToken(t)))
}
private transformOutputToken(token: TypeScriptToken): TypeScriptToken
{
//This is needed because some places already have multiples whitespaces
if (token.syntaxKind === SyntaxKind.WhitespaceTrivia && token.content.match(/\s\s+/)) {
return new TypeScriptToken(token.syntaxKind, " ", token.level);
}
return token;
}
}