Skip to content
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
4 changes: 0 additions & 4 deletions src/LuaAST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,6 @@ function getSourcePosition(sourceNode: ts.Node): TextRange | undefined {
}

export function getOriginalPos(node: Node): TextRange {
while (node.line === undefined && node.parent !== undefined) {
node = node.parent;
}

return { line: node.line, column: node.column };
}

Expand Down
86 changes: 74 additions & 12 deletions src/LuaPrinter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as path from "path";

import {SourceNode, SourceMapGenerator} from "source-map";
import { Mapping, SourceNode, SourceMapGenerator } from "source-map";

import * as tstl from "./LuaAST";
import { CompilerOptions, LuaLibImportKind } from "./CompilerOptions";
Expand Down Expand Up @@ -59,22 +59,23 @@ export class LuaPrinter {

const rootSourceNode = this.printImplementation(block, luaLibFeatures, sourceFile);

const codeWithSourceMap = rootSourceNode
// TODO is the file: part really required? and should this be handled in the printer?
.toStringWithSourceMap({file: path.basename(sourceFile, path.extname(sourceFile)) + ".lua"});
const sourceRoot = this.options.sourceRoot
|| (this.options.outDir ? path.relative(this.options.outDir, this.options.rootDir || process.cwd()) : ".");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tsc makes the source filename either only the filename or maybe a relative path (Definitely no absolute paths), but this does produce absolute paths, which should probably be changed.


let codeResult = codeWithSourceMap.code;
const sourceMap = this.buildSourceMap(sourceFile, sourceRoot, rootSourceNode);

let codeResult = rootSourceNode.toString();

if (this.options.inlineSourceMap) {
codeResult += "\n" + this.printInlineSourceMap(codeWithSourceMap.map);
codeResult += "\n" + this.printInlineSourceMap(sourceMap);
}

if (this.options.sourceMapTraceback) {
const stackTraceOverride = this.printStackTraceOverride(rootSourceNode);
codeResult = codeResult.replace("{#SourceMapTraceback}", stackTraceOverride);
}

return [codeResult, codeWithSourceMap.map.toString()];
return [codeResult, sourceMap.toString()];
}

private printInlineSourceMap(sourceMap: SourceMapGenerator): string {
Expand Down Expand Up @@ -140,7 +141,7 @@ export class LuaPrinter {
header += "{#SourceMapTraceback}\n";
}

const fileBlockNode = this.createSourceNode(block, this.printBlock(block));
const fileBlockNode = this.printBlock(block);

return this.concatNodes(header, fileBlockNode);
}
Expand Down Expand Up @@ -172,7 +173,7 @@ export class LuaPrinter {
}

protected printBlock(block: tstl.Block): SourceNode {
return this.createSourceNode(block, this.printStatementArray(block.statements));
return this.concatNodes(...this.printStatementArray(block.statements));
}

private statementMayRequireSemiColon(statement: tstl.Statement): boolean {
Expand Down Expand Up @@ -428,7 +429,7 @@ export class LuaPrinter {
}

public printExpressionStatement(statement: tstl.ExpressionStatement): SourceNode {
return this.concatNodes(this.indent(), this.printExpression(statement.expression));
return this.createSourceNode(statement, [this.indent(), this.printExpression(statement.expression)]);
}

// Expressions
Expand Down Expand Up @@ -626,7 +627,7 @@ export class LuaPrinter {

chunks.push(this.printExpression(expression.expression), "(", ...this.joinChunks(", ", parameterChunks), ")");

return this.concatNodes(...chunks);
return this.createSourceNode(expression, chunks);
}

public printMethodCallExpression(expression: tstl.MethodCallExpression): SourceNode {
Expand All @@ -638,7 +639,10 @@ export class LuaPrinter {

const name = this.printIdentifier(expression.name);

return this.concatNodes(prefix, ":", name, "(", ...this.joinChunks(", ", parameterChunks), ")");
return this.createSourceNode(
expression,
[prefix, ":", name, "(", ...this.joinChunks(", ", parameterChunks), ")"]
);
}

public printIdentifier(expression: tstl.Identifier): SourceNode {
Expand Down Expand Up @@ -689,4 +693,62 @@ export class LuaPrinter {
}
return result;
}

// The key difference between this and SourceNode.toStringWithSourceMap() is that SourceNodes with null line/column
// will not generate 'empty' mappings in the source map that point to nothing in the original TS.
private buildSourceMap(sourceFile: string, sourceRoot: string, rootSourceNode: SourceNode): SourceMapGenerator {
const map = new SourceMapGenerator({
file: path.basename(sourceFile, path.extname(sourceFile)) + ".lua",
sourceRoot,
});

let generatedLine = 1;
let generatedColumn = 0;
let currentMapping: Mapping | undefined;

const isNewMapping = (sourceNode: SourceNode) => {
if (sourceNode.line === null) {
return false;
}
if (currentMapping === undefined) {
return true;
}
if (currentMapping.generated.line === generatedLine
&& currentMapping.generated.column === generatedColumn)
{
return false;
}
return (currentMapping.original.line !== sourceNode.line
|| currentMapping.original.column !== sourceNode.column);
};

const build = (sourceNode: SourceNode) => {
if (isNewMapping(sourceNode)) {
currentMapping = {
source: sourceNode.source,
original: { line: sourceNode.line, column: sourceNode.column },
generated: { line: generatedLine, column: generatedColumn },
};
map.addMapping(currentMapping);
}

for (const chunk of sourceNode.children) {
if (typeof chunk === "string") {
const lines = (chunk as string).split("\n");
if (lines.length > 1) {
generatedLine += lines.length - 1;
generatedColumn = 0;
currentMapping = undefined; // Mappings end at newlines
}
generatedColumn += lines[lines.length - 1].length;

} else {
build(chunk);
}
}
};
build(rootSourceNode);

return map;
}
}
Loading