1222 строки
49 KiB
TypeScript
1222 строки
49 KiB
TypeScript
///<reference path='refs.ts'/>
|
||
|
||
module TDev
|
||
{
|
||
export class Renderer
|
||
extends AST.NodeVisitor
|
||
{
|
||
static spacedText(t:string) {
|
||
switch (t) {
|
||
case "0":
|
||
case "1":
|
||
case "2":
|
||
case "3":
|
||
case "4":
|
||
case "5":
|
||
case "6":
|
||
case "7":
|
||
case "8":
|
||
case "9":
|
||
case "(":
|
||
case ")":
|
||
case ".":
|
||
case "":
|
||
case " ":
|
||
return t;
|
||
case ",":
|
||
return ", ";
|
||
case "not":
|
||
return "not ";
|
||
default:
|
||
return " " + t + " ";
|
||
}
|
||
}
|
||
|
||
static opName(s:string) {
|
||
if (!AST.proMode) return s
|
||
switch (s) {
|
||
case "\u2260": return "!="
|
||
case "\u2264": return "<="
|
||
case "\u2265": return ">="
|
||
case "=": return "=="
|
||
default:
|
||
return s
|
||
}
|
||
}
|
||
|
||
static quoteString(s:string) {
|
||
var r = "";
|
||
for (var i = 0; i < s.length; ++i) {
|
||
var c = s.charAt(i);
|
||
var o:string;
|
||
switch (c) {
|
||
case "\t": o = "\\t"; break;
|
||
case "\"": o = "\\\""; break;
|
||
case "\\": o = "\\\\"; break;
|
||
case "\r": o = "\\r"; break;
|
||
case "\n": o = "\\n"; break;
|
||
default: o = c; break;
|
||
}
|
||
r = r + o;
|
||
}
|
||
return r;
|
||
}
|
||
|
||
constructor() {
|
||
super()
|
||
}
|
||
|
||
static tdiv(cl: string, s: string) { return "<div class='" + cl + "'>" + s + "</div>"; }
|
||
static tspan(cl: string, s: string) { return "<span class='" + cl + "'>" + Util.htmlEscape(s) + "</span>"; }
|
||
static tspanRaw(cl: string, s: string) { return "<span class='" + cl + "'>" + s + "</span>"; }
|
||
|
||
public kw(k:string) { return Renderer.tspan("kw", " " + k + " "); }
|
||
public kw0(k:string) { return Renderer.tspan("kw", k); }
|
||
public greyKw(k:string) { return Renderer.tspan("greyed", " " + k + " "); }
|
||
public id(kw:string) { return Util.htmlEscape(kw); }
|
||
public name(name: string) { return Renderer.tspan("name", Util.htmlEscape(name));}
|
||
public kind(k:Kind) { return Util.htmlEscape(k.toString()); }
|
||
public op(kw:string) { return Util.htmlEscape(Renderer.spacedText(kw)); }
|
||
public st(kw:string) { return Renderer.tspan("st", kw); }
|
||
public tline(s:string) {
|
||
return Renderer.tdiv("line", s.trim() + (this.showDiff || AST.blockMode ? "" :
|
||
"<span class='lineSpacer'> </span>")) + "<br/>"; }
|
||
|
||
public isAux = false;
|
||
public shortNameHeuristicsApplied = false;
|
||
public shortNameHeuristics = false;
|
||
public stringLimit = 30;
|
||
public codeReplacement:string;
|
||
public mdComments = new MdComments(null, null);
|
||
public renderingSnippet = false;
|
||
public highlightLevel = 0;
|
||
public showDiff = false;
|
||
public hideErrors = false;
|
||
public formatComments = true;
|
||
public skipComments = false;
|
||
public numDiffs = 0;
|
||
|
||
public interpretedComment(n:AST.Comment)
|
||
{
|
||
if (!this.renderingSnippet) return false;
|
||
var m = /^\s*\{(\/)?highlight\}\s*$/.exec(n.text);
|
||
if (m) {
|
||
if (m[1]) this.highlightLevel--;
|
||
else this.highlightLevel++;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
public renderBlock(lst:AST.Block)
|
||
{
|
||
if (this.isAux) return "";
|
||
|
||
if (this.showDiff)
|
||
return this.renderSnippet(lst.stmtsWithDiffs())
|
||
return this.renderSnippet(lst.stmts);
|
||
}
|
||
|
||
public renderDecl(d:AST.Decl)
|
||
{
|
||
this.highlightLevel = 0;
|
||
this.renderingSnippet = true;
|
||
try {
|
||
return Renderer.tdiv("decl", d.accept(this))
|
||
} finally {
|
||
this.renderingSnippet = false;
|
||
}
|
||
}
|
||
|
||
public renderSnippet(s:AST.AstNode[])
|
||
{
|
||
var res = "";
|
||
var prevHl = this.highlightLevel
|
||
var prevSnip = this.renderingSnippet
|
||
this.highlightLevel = 0;
|
||
this.renderingSnippet = true;
|
||
try {
|
||
for (var i = 0; i < s.length; ++i)
|
||
res += s[i].accept(this);
|
||
return Renderer.tdiv("block", res);
|
||
} finally {
|
||
this.renderingSnippet = prevSnip
|
||
this.highlightLevel = prevHl
|
||
}
|
||
}
|
||
|
||
private wrapError(n:AST.AstNode, r:string)
|
||
{
|
||
if (n.getError() != null)
|
||
return "<span class='errorSq'>" + r + "</span>";
|
||
else
|
||
return r;
|
||
}
|
||
|
||
public renderTokens(t:AST.Token[])
|
||
{
|
||
var res = "";
|
||
if (this.shortNameHeuristics) {
|
||
var sna = -2;
|
||
for (var i = 0; i < t.length; ++i) {
|
||
if (t[i] instanceof AST.PropertyRef && sna == i - 1) {
|
||
res += this.id("\u200A" + t[i].getText())
|
||
} else {
|
||
this.shortNameHeuristicsApplied = false;
|
||
res += t[i].accept(this);
|
||
if (this.shortNameHeuristicsApplied) sna = i;
|
||
}
|
||
}
|
||
} else {
|
||
for (var i = 0; i < t.length; ++i)
|
||
res += t[i].accept(this);
|
||
}
|
||
return res;
|
||
}
|
||
|
||
private renderArtUrl(kind: Kind, v: string): string {
|
||
if (kind == api.core.String && v)
|
||
return Renderer.tdiv("stringLiteral",
|
||
Util.htmlEscape(
|
||
TDev.RT.String_.trim_overflow(TDev.RT.String_.valueFromArtUrl(v), 400)
|
||
)
|
||
);
|
||
return this.renderString(v, 200);
|
||
}
|
||
|
||
public renderBitmatrix(v: string): string {
|
||
var bits: boolean[][] = (v || "").trim().split("\n").map(row => row.split(/[\s\r\n]+/).map(s => {
|
||
var x = parseInt(s); if (isNaN(x)) x = 0;
|
||
return !!x;
|
||
}));
|
||
|
||
var maxCols = 45;
|
||
var f = 50; var f2 = f / 2;
|
||
var r1 = "";
|
||
var r0 = "";
|
||
var w = 0;
|
||
var rows = bits.length;
|
||
if (!v) rows = 5;
|
||
var h = rows * f;
|
||
var ellipse = false;
|
||
for (var y = 0; y < rows; ++y) {
|
||
var row = bits[y] || [];
|
||
var cols = Math.max(rows, Math.min(row.length, maxCols));
|
||
w = Math.max(w, cols * f + Math.floor(cols /5) * f);
|
||
ellipse = ellipse || cols < row.length;
|
||
for (var x = 0; x < cols; ++x) {
|
||
var bit = row[x];
|
||
var left = f * x + Math.floor(x / 5) * f;
|
||
var top = f * y;
|
||
var width = f2;
|
||
if (bit) { width += 4; left -= 2; top -= 2; }
|
||
var path = Util.fmt(" M {0} {1} {2} {3} {4} {5} {6} {7} Z",
|
||
left, top, left+width, top, left+width, top+width, left, top+width);
|
||
if (bit) r1 += path; else r0 += path;
|
||
}
|
||
}
|
||
var viewPort = Util.fmt("0 0 {0} {1}", w, h);
|
||
var svg = Util.fmt("<path class='biton' d='{0}'/><path class='bitoff' d='{1}'/>", r1, r0);
|
||
var result = Util.fmt("<span class='kbm' style='width:{0}em'>{1}</span>",
|
||
w / h + 0.3,
|
||
SVG.svgBoilerPlate(viewPort, svg));
|
||
if (ellipse) result += Renderer.tspan("stringLiteral", "...");
|
||
return result;
|
||
}
|
||
|
||
public renderString(v:string, lim = this.stringLimit) : string
|
||
{
|
||
return Renderer.tspan("stringLiteral", "\"" + Renderer.quoteString(TDev.RT.String_.trim_overflow(v, lim)) + "\"");
|
||
}
|
||
|
||
public visitRecordName(n: AST.RecordName) {
|
||
return this.id(n.data);
|
||
}
|
||
|
||
public visitFieldName(n: AST.FieldName) {
|
||
return this.id(n.data) + this.op(":");
|
||
}
|
||
|
||
public visitLiteral(n:AST.Literal)
|
||
{
|
||
var v = n.data;
|
||
var div:string;
|
||
|
||
switch (typeof v) {
|
||
case "number":
|
||
v = v + "";
|
||
Util.die();
|
||
break;
|
||
case "boolean":
|
||
div = Renderer.tspan("kw", v ? "true" : "false");
|
||
break;
|
||
case "string":
|
||
if (/^bitmatrix$/.test(n.languageHint)) div = this.renderBitmatrix(v);
|
||
else div = this.renderString(v);
|
||
break;
|
||
default:
|
||
Util.die();
|
||
}
|
||
return this.wrapError(n, div);
|
||
}
|
||
|
||
public visitPropertyRef(n:AST.PropertyRef)
|
||
{
|
||
var nm = n.getText();
|
||
var arrow = n.getOrMakeProp().getArrow()
|
||
if (n.skipArrow) arrow = "\u200A"
|
||
return this.wrapError(n, this.id(arrow + nm));
|
||
}
|
||
|
||
public visitThingRef(n:AST.ThingRef)
|
||
{
|
||
if (this.codeReplacement && !n.forceLocal && n.getText() == 'code')
|
||
return this.codeReplacement;
|
||
|
||
var sn = n.shortName();
|
||
|
||
if (!sn && this.shortNameHeuristics && !n.forceLocal) {
|
||
var knd = api.getKind(n.getText())
|
||
if (knd && knd instanceof ThingSetKind)
|
||
sn = knd.shortName();
|
||
this.shortNameHeuristicsApplied = true;
|
||
}
|
||
|
||
if (n.def instanceof AST.PlaceholderDef) {
|
||
var pl = <AST.PlaceholderDef>n.def;
|
||
return this.wrapError(n, Util.fmt("<span class='placeholder'>{0:q}</span>", pl.label || pl.getKind().toString()))
|
||
}
|
||
|
||
var content = "";
|
||
if (!sn) content = this.id(n.getText());
|
||
else {
|
||
if (Browser.isAndroid) {
|
||
var svg = SVG.codeSignHtml(sn);
|
||
if (svg) content = "<span class='svg-symbol'>" + svg + "</span>";
|
||
else content = Renderer.tspan("id symbol", sn);
|
||
} else {
|
||
content = Renderer.tspan("id symbol", sn);
|
||
}
|
||
}
|
||
return this.wrapError(n, content);
|
||
}
|
||
|
||
public visitOperator(n:AST.Operator)
|
||
{
|
||
var s = n.getText();
|
||
var r = this.op(Renderer.opName(s));
|
||
if (/^[a-z]+$/.test(s))
|
||
r = this.kw(s);
|
||
|
||
var args = n.getFunArgs()
|
||
if (args) {
|
||
if (args.length == 1) s = args[0]
|
||
else s = "(" + args.join(", ") + ")"
|
||
r = this.id(s + " ⇒ ")
|
||
}
|
||
|
||
//if (/^[0-9\.]$/.test(s))
|
||
// r = Renderer.tspan("numberLiteral", s)
|
||
return this.wrapError(n, r);
|
||
}
|
||
|
||
private diffWords(tokens:string[])
|
||
{
|
||
var res = ""
|
||
|
||
for (var i = 0; i < tokens.length; i += 2) {
|
||
if (tokens[i] == null || tokens[i + 1] == null)
|
||
break;
|
||
res += tokens[i + 1]
|
||
}
|
||
|
||
if (i >= tokens.length) return res;
|
||
|
||
this.numDiffs++
|
||
res = ""
|
||
for (var i = 0; i < tokens.length; i += 2) {
|
||
if (tokens[i] != null && tokens[i + 1] != null)
|
||
res += Renderer.tspanRaw("diffTokenUnchanged", tokens[i + 1])
|
||
else if (tokens[i] != null)
|
||
res += Renderer.tspanRaw("diffTokenRemoved", tokens[i])
|
||
else
|
||
res += Renderer.tspanRaw("diffTokenAdded", tokens[i+1])
|
||
}
|
||
|
||
return Renderer.tspanRaw("diffTokensChanged", res)
|
||
}
|
||
|
||
public renderDiffTokens(tokens:AST.Token[])
|
||
{
|
||
var toks = tokens.map(t => t ? <string>t.accept(this) : null)
|
||
return this.diffWords(toks)
|
||
}
|
||
|
||
public visitExprHolder(e:AST.ExprHolder)
|
||
{
|
||
return this.renderExprHolder(e, e.tokens)
|
||
}
|
||
|
||
public renderExprHolder(e:AST.ExprHolder, tokens:AST.Token[]):string
|
||
{
|
||
if (this.showDiff && e.diffTokens) return this.renderDiffTokens(e.diffTokens)
|
||
|
||
var res: string = this.renderTokens(tokens);
|
||
if (!e.profilingExprData && !e.debuggingData)
|
||
return res;
|
||
if (e.profilingExprData) {
|
||
var index = Math.floor(e.profilingExprData.duration / AST.ExprHolder.profilingDurationBucketSize);
|
||
var color: string = AST.ExprHolder.heatmapColors[index];
|
||
return "<span style='background-color:" + color + "'>" + res + "</span>" +
|
||
"<span class='profile' style='background-color:" + color + "'>" +
|
||
Math.round(e.profilingExprData.duration) + "ms/" + Math.round(e.profilingExprData.count) + "</span>";
|
||
}
|
||
|
||
if (e.debuggingData) {
|
||
if (e.debuggingData.critical && e.debuggingData.max) {
|
||
var scorePartial = e.debuggingData.critical / e.debuggingData.max.critical;
|
||
var score = Math.floor(scorePartial * 27); // there are 28 colors, first of them is white
|
||
var color: string = AST.ExprHolder.heatmapColors[score];
|
||
var ret = "<span style='background-color:" + color + "'>" + res + "</span>";
|
||
if (dbg) ret += ("<span class='profile' style='background-color:" + color + "'>" + Math.floor(scorePartial*100) + "%</span>");
|
||
return ret;
|
||
} else if (e.debuggingData.visited) {
|
||
return "<span style='background-color:#eaf3ff'>" + res + "</span>";
|
||
} else if (e.debuggingData.alwaysTrue) {
|
||
return "<span style='background-color:#daffda'>" + res + "</span>";
|
||
} else if (e.debuggingData.alwaysFalse) {
|
||
return "<span style='background-color:#ffdada'>" + res + "</span>";
|
||
} else return res;
|
||
}
|
||
}
|
||
|
||
public diffClass(n:AST.Stmt)
|
||
{
|
||
var diffClass = ""
|
||
if (this.showDiff) {
|
||
if (n.diffStatus < 0) {
|
||
this.numDiffs++
|
||
return " diffStmtRemoved";
|
||
}
|
||
else if (n.diffStatus > 0) {
|
||
this.numDiffs++
|
||
return " diffStmtAdded";
|
||
}
|
||
}
|
||
return "";
|
||
}
|
||
|
||
public diffMoveMarker(n:AST.Stmt)
|
||
{
|
||
if (this.showDiff) {
|
||
var moveId = 0;
|
||
moveId = -n.diffStatus - 2;
|
||
if (moveId < 0 && n.diffAltStmt)
|
||
moveId = -n.diffAltStmt.diffStatus - 2;
|
||
if (moveId >= 0)
|
||
return "<div class='diffMoveMarker'>" + moveId + "</div>";
|
||
}
|
||
return "";
|
||
}
|
||
|
||
public stmtClass(n:AST.Stmt)
|
||
{
|
||
var tp = n.nodeType()
|
||
var cat = "other"
|
||
|
||
switch (tp) {
|
||
case "inlineActions":
|
||
case "exprStmt":
|
||
cat = "exprStmt"; break;
|
||
|
||
case "actionHeader":
|
||
case "inlineAction":
|
||
cat = "action"; break;
|
||
|
||
case "recordDef":
|
||
case "libraryRef":
|
||
case "globalDef":
|
||
cat = "decl"; break;
|
||
|
||
case "while":
|
||
case "for":
|
||
case "foreach":
|
||
cat = "loop"; break;
|
||
|
||
case "elseIf":
|
||
case "if":
|
||
cat = "if"; break;
|
||
|
||
case "recordField":
|
||
case "actionParameter":
|
||
cat = "field"; break;
|
||
}
|
||
|
||
return " stmt-" + cat + " stmt-" + tp
|
||
}
|
||
|
||
public stmt(n:AST.Stmt, args:string, f:(e:HTMLElement)=>void = undefined, blockNode:AST.Stmt = null)
|
||
{
|
||
if (this.showDiff)
|
||
args = Renderer.tdiv(this.diffClass(n) + this.stmtClass(blockNode || n), args + this.diffMoveMarker(n))
|
||
if (this.highlightLevel > 0)
|
||
return Renderer.tdiv("code-highlight", args) + "<!-- HL -->";
|
||
return Renderer.tdiv("stmt" + this.stmtClass(blockNode || n), args);
|
||
}
|
||
|
||
private expr(e:AST.ExprHolder)
|
||
{
|
||
if (e.isPlaceholder())
|
||
return this.id("...");
|
||
else
|
||
return this.dispatch(e);
|
||
}
|
||
|
||
public message(cls:string, content:string)
|
||
{
|
||
return Renderer.tdiv(cls, content) + "<br class='errorBr'/>";
|
||
}
|
||
|
||
public errorNum(num:string)
|
||
{
|
||
if (!num) return "";
|
||
else return "<span class='errorNumber'> [" + num + "]</span>";
|
||
}
|
||
|
||
public errorHTML(node:AST.AstNode)
|
||
{
|
||
if (node instanceof AST.ExprStmt) {
|
||
var exprStmt = <AST.ExprStmt>node;
|
||
if (exprStmt.expr) {
|
||
var dd = exprStmt.expr.debuggingData;
|
||
if (dd && dd.errorMessage)
|
||
return "<span class='symbol'>\u26a1</span> " + Util.htmlEscape(dd.errorMessage);
|
||
}
|
||
}
|
||
var err = node.getError();
|
||
if (err != null) {
|
||
var errNum = "";
|
||
err = err.replace(/^(TD\d\d\d): /, (mtch, en) => {
|
||
errNum = en;
|
||
return "";
|
||
});
|
||
return Util.htmlEscape("\u2639 " + err) + this.errorNum(errNum)
|
||
} else {
|
||
return ""
|
||
}
|
||
}
|
||
|
||
public possibleError(node:AST.AstNode):any {
|
||
if (this.isAux || this.showDiff || this.hideErrors) return "";
|
||
var err = node.getError();
|
||
var res = "";
|
||
var errH = this.errorHTML(node)
|
||
if (errH)
|
||
res += this.message(node.errorIsOk ? "hintMessage" : "errorMessage", errH)
|
||
|
||
if (res == "" && node instanceof AST.Stmt) {
|
||
var tw = (<AST.Stmt>node).tutorialWarning
|
||
if (tw) {
|
||
tw.split("\n").forEach(m => {
|
||
if (m)
|
||
res += this.message("errorMessage", Util.htmlEscape("[T] " + m))
|
||
})
|
||
}
|
||
}
|
||
|
||
if (res == "" && node instanceof AST.Stmt) {
|
||
var hint = (<AST.Stmt>node).getHint()
|
||
if (hint)
|
||
hint.split("\n").forEach(h => {
|
||
res += this.message("hintMessage", Util.htmlEscape("\u270e " + h))
|
||
})
|
||
}
|
||
|
||
if (node.annotations)
|
||
node.annotations.forEach(a => {
|
||
res += this.message(a.category == "error" ? "errorMessage fromPlugin" : "hintMessage fromPlugin",
|
||
"<span class='svg-symbol'>" + SVG.getIconSVGCore("plug,#7057D3,clip=100") + "</span> " +
|
||
Util.htmlEscape(a.message))
|
||
})
|
||
|
||
return res;
|
||
}
|
||
|
||
private renderExprStmt(n:AST.ExprStmt, suffix = "")
|
||
{
|
||
if (n.isPlaceholder()) {
|
||
if (AST.proMode)
|
||
return this.tline("<span class='greyed'> ;</span>");
|
||
else
|
||
return this.tline("<span class='greyed'>do nothing</span>");
|
||
} else {
|
||
var toks = this.renderExprHolder(n.expr, n.expr.tokens)
|
||
toks += suffix
|
||
if (n.isVarDef())
|
||
toks = this.kw("var ") + toks;
|
||
else if (!AST.blockMode && n.isPagePush())
|
||
toks = this.kw("push ") + toks;
|
||
return this.tline(toks) + this.possibleError(n);
|
||
}
|
||
}
|
||
|
||
public visitExprStmt(n:AST.ExprStmt)
|
||
{
|
||
if (!this.showDiff && this.formatComments) {
|
||
var doc = n.docText()
|
||
if (doc != null) {
|
||
var inner = this.tline(Renderer.tdiv("md-comment",
|
||
this.mdComments.formatText(doc)))
|
||
return this.stmt(n, inner + this.possibleError(n));
|
||
}
|
||
}
|
||
return this.stmt(n, this.renderExprStmt(n))
|
||
}
|
||
|
||
public visitOptionalParameter(o:AST.OptionalParameter)
|
||
{
|
||
var name = this.diffTwoWords(o.getName(), o.diffAltStmt ? (<AST.OptionalParameter>o.diffAltStmt).getName() : null)
|
||
return this.stmt(o, this.tline(this.kw("with ") + name + this.op("=") + this.dispatch(o.expr)) +
|
||
this.possibleError(o))
|
||
}
|
||
|
||
public visitInlineAction(a:AST.InlineAction)
|
||
{
|
||
var renderHead = (a:AST.InlineAction) => {
|
||
var r = [this.kw(a.isOptional ? "with " : "where "), this.id(a.name.getName())]
|
||
|
||
var renderParms = (parms:AST.LocalDef[]) => {
|
||
var first = true;
|
||
r.push(this.op("("))
|
||
parms.forEach((p) => {
|
||
if (!first) r.push(this.op(", "))
|
||
first = false;
|
||
r.push(this.id(p.getName()), this.op(":"), this.kind(p.getKind()))
|
||
});
|
||
r.push(this.op(")"))
|
||
}
|
||
|
||
renderParms(a.inParameters);
|
||
if (a.outParameters.length > 0) {
|
||
r.push(this.kw(" returns "))
|
||
renderParms(a.outParameters);
|
||
}
|
||
|
||
if (AST.proMode)
|
||
r.push(this.st(" {"))
|
||
else
|
||
r.push(this.kw(" is"))
|
||
|
||
return r;
|
||
}
|
||
|
||
var ss = this.diffLine(a, a.diffAltStmt, renderHead) +
|
||
this.possibleError(a) +
|
||
this.renderBlock(a.body) +
|
||
this.endKeyword("")
|
||
return this.stmt(a, ss);
|
||
}
|
||
|
||
public visitInlineActions(n:AST.InlineActions)
|
||
{
|
||
var last = n.implicitAction()
|
||
var hasImplicit = !!last
|
||
|
||
var suff = ""
|
||
if (!this.isAux && hasImplicit && n.actions.stmts.length == 1)
|
||
suff = AST.proMode ? this.st(" {") : this.kw("do")
|
||
|
||
var expr = this.renderExprStmt(n, suff);
|
||
|
||
if (!this.isAux) {
|
||
var body = n.actions.stmts.slice(0)
|
||
if (hasImplicit) {
|
||
body.pop()
|
||
expr += this.renderSnippet(body)
|
||
if (!suff)
|
||
expr += this.tline(AST.proMode ? this.st(" {") : this.kw("do"))
|
||
expr += this.possibleError(last)
|
||
expr += this.renderBlock(last.body)
|
||
expr += this.tline(AST.proMode ? this.st("}") : this.kw("end"))
|
||
} else {
|
||
expr += this.renderSnippet(body)
|
||
if (!AST.proMode && body.length > 0)
|
||
expr += this.endKeyword("")
|
||
}
|
||
}
|
||
return this.stmt(n, expr)
|
||
}
|
||
|
||
public visitWhile(n:AST.While)
|
||
{
|
||
return this.stmt(n,
|
||
(AST.proMode ?
|
||
this.tline(this.kw("while") + this.st(" (") + this.expr(n.condition) + this.st(") {")) :
|
||
this.tline(this.kw("while") + this.expr(n.condition) + this.kw("do"))) +
|
||
this.possibleError(n) +
|
||
this.renderBlock(n.body) +
|
||
this.endKeyword("while"));
|
||
}
|
||
|
||
public visitBox(n:AST.Box)
|
||
{
|
||
return this.stmt(n,
|
||
this.tline(this.kw("boxed") + (AST.proMode ? this.st("{") : "")) +
|
||
this.possibleError(n) +
|
||
this.renderBlock(n.body) +
|
||
this.endKeyword("boxed"));
|
||
}
|
||
|
||
|
||
public visitComment(n:AST.Comment)
|
||
{
|
||
function tokenize(n:AST.Comment)
|
||
{
|
||
var toks = ["<div translate=yes class='md-comment'>"]
|
||
n.text.replace(/[^\s]+\s*/g, (m) => {
|
||
toks.push(Util.htmlEscape(m))
|
||
return ""
|
||
})
|
||
toks.push("</div>")
|
||
return toks
|
||
}
|
||
|
||
var inner = ""
|
||
if (this.showDiff) {
|
||
inner = this.diffLine(n, n.diffAltStmt, tokenize)
|
||
} else {
|
||
if (this.interpretedComment(n)) return "";
|
||
if (!this.skipComments) {
|
||
if (this.formatComments)
|
||
inner = this.tline(Renderer.tdiv("md-comment", this.mdComments.formatText(n.text, n)))
|
||
else
|
||
inner = this.tline("<span class='comment'> " + Util.formatText(n.text) + "</span>")
|
||
}
|
||
}
|
||
return this.stmt(n, inner + this.possibleError(n));
|
||
}
|
||
|
||
public endKeyword(name:string)
|
||
{
|
||
if (AST.proMode)
|
||
return this.tline(this.st("}")/* + this.greyKw(" // " + name) */);
|
||
else
|
||
return this.tline(this.kw("end ") + this.greyKw(name));
|
||
}
|
||
|
||
public visitFor(n:AST.For)
|
||
{
|
||
var idiff = this.diffTwoWords(n.boundLocal.getName(), n.diffAltStmt ? (<AST.For>n.diffAltStmt).boundLocal.getName() : null)
|
||
var i = this.id(n.boundLocal.getName())
|
||
return this.stmt(n,
|
||
(AST.proMode ?
|
||
//this.tline(this.kw("for") + this.st("(") + this.kw0("var ") + idiff + Renderer.tspan("greyed", " = 0; " + i + " < ") +
|
||
// this.expr(n.upperBound) + Renderer.tspan("greyed", "; " + i + " = " + i + " + 1") + this.st(") {")) :
|
||
this.tline(this.kw("for") + this.st("(") + this.kw0("var ") + idiff + this.st(" < ") +
|
||
this.expr(n.upperBound) + this.st(") {")) :
|
||
this.tline(this.kw("for") + this.id("0") + this.op("\u2264") + idiff + this.op("<") +
|
||
this.expr(n.upperBound) + this.kw("do"))
|
||
) +
|
||
this.possibleError(n) +
|
||
this.renderBlock(n.body) +
|
||
this.endKeyword("for")
|
||
);
|
||
}
|
||
|
||
public visitForeach(n:AST.Foreach)
|
||
{
|
||
var hasWhere = n.conditions.stmts.length > 0
|
||
return this.stmt(n,
|
||
this.tline(this.kw("for each ") + (AST.proMode ? this.st("(") + this.kw0("var ") : "") +
|
||
this.diffTwoWords(n.boundLocal.getName(), n.diffAltStmt ? (<AST.Foreach>n.diffAltStmt).boundLocal.getName() : null) +
|
||
this.kw("in") + this.expr(n.collection) +
|
||
(AST.proMode ? this.st(")") + (hasWhere ? "" : this.st(" {"))
|
||
: (!hasWhere ? this.kw("do") : ""))) +
|
||
this.possibleError(n) +
|
||
(hasWhere ?
|
||
this.renderBlock(n.conditions) +
|
||
this.tline(AST.proMode ? this.st("{") : this.kw("do")) : "") +
|
||
this.renderBlock(n.body) +
|
||
this.endKeyword("for each")
|
||
);
|
||
}
|
||
|
||
public visitWhere(n:AST.Where)
|
||
{
|
||
return this.stmt(n, this.tline(this.kw("where") + this.expr(n.condition)) + this.possibleError(n));
|
||
}
|
||
|
||
public visitAnyIf(n:AST.If):string
|
||
{
|
||
var children =
|
||
AST.proMode ?
|
||
this.tline((n.isElseIf ? this.st("}") + this.kw("else if") : this.kw("if")) + this.st(" (") + this.expr(n.rawCondition) + this.st(") {")) :
|
||
this.tline(this.kw(n.isElseIf ? "else if" : "if") + this.expr(n.rawCondition) + this.kw("then"))
|
||
|
||
children += this.possibleError(n);
|
||
|
||
if (n.isTopCommentedOut()) {
|
||
children += Renderer.tdiv("block-comment", this.renderBlock(n.rawThenBody))
|
||
// children += this.endKeyword("if false")
|
||
return this.stmt(n, children);
|
||
} else {
|
||
children += this.renderBlock(n.rawThenBody);
|
||
}
|
||
|
||
if (this.isAux) return children;
|
||
|
||
if (n.displayElse) {
|
||
if (n.rawElseBody.isBlockPlaceholder()) {
|
||
return this.stmt(n, children) +
|
||
this.stmt(n.rawElseBody.stmts[0] || n,
|
||
AST.proMode ?
|
||
this.tline(this.st("}") + this.kw("else") + this.st("{ }")) :
|
||
this.tline(this.kw("else") + Renderer.tspan("greyed", "do nothing") + this.kw(" end ") + this.greyKw("if")),
|
||
(e:HTMLElement) => e.setFlag("elseDoNothing", true), n);
|
||
} else {
|
||
if (AST.proMode)
|
||
children += this.tline(this.st("}") + this.kw("else") + this.st("{"));
|
||
else
|
||
children += this.tline(this.kw("else"));
|
||
children += this.renderBlock(n.rawElseBody);
|
||
children += this.endKeyword("if")
|
||
}
|
||
}
|
||
|
||
return this.stmt(n, children);
|
||
}
|
||
|
||
public visitActionParameter(n:AST.ActionParameter)
|
||
{
|
||
|
||
var finaltok = this.op((<AST.Block>n.parent).stmts.peek() == n ? ")" : ",")
|
||
var str = this.diffLine(n, n.diffAltStmt,
|
||
n => [this.id(n.getName()), this.op(":"), this.kind(n.getKind()), finaltok])
|
||
return this.stmt(n, str + this.possibleError(n));
|
||
}
|
||
|
||
private actionKeyword(a:AST.Action)
|
||
{
|
||
if (a.isEvent()) return "event";
|
||
else if (a.isPage()) return "page";
|
||
return "function";
|
||
}
|
||
|
||
private renderActionHeader(n:AST.ActionHeader)
|
||
{
|
||
var inParms = "";
|
||
var returns = "";
|
||
var outParms = "";
|
||
|
||
var hd = "";
|
||
|
||
if (n.action.isPrivate) hd += this.kw("private ");
|
||
//if (!n.action.isEvent() && !n.action.isPrivate) hd += this.kw("public ");
|
||
if (n.action.isAtomic && !n.action.isPage()) hd += this.kw("atomic ");
|
||
if (n.action.isTest()) hd += this.kw("test ");
|
||
if (n.action.isOffline) hd += this.kw("offline ");
|
||
if (n.action.isQuery) hd += this.kw("query ");
|
||
|
||
hd += this.kw(this.actionKeyword(n.action));
|
||
|
||
if (n.action.isActionTypeDef())
|
||
hd += this.kw(" type")
|
||
|
||
hd += this.id(n.getName())
|
||
|
||
if (n.action.hasInParameters()) {
|
||
hd += this.op(" (");
|
||
inParms = this.renderBlock(n.inParameters);
|
||
} else {
|
||
hd += this.op(" ()");
|
||
}
|
||
if (n.action.hasOutParameters()) {
|
||
returns = this.tline(this.kw("returns") + this.op(" ("));
|
||
outParms = this.renderBlock(n.outParameters);
|
||
}
|
||
|
||
if (AST.proMode && !inParms && !outParms)
|
||
hd += this.st(" {")
|
||
|
||
return this.tline(hd) +
|
||
inParms +
|
||
returns +
|
||
outParms +
|
||
this.possibleError(n.action);
|
||
}
|
||
|
||
public addModelFieldHint()
|
||
{
|
||
return ""
|
||
}
|
||
|
||
public visitActionHeader(n:AST.ActionHeader)
|
||
{
|
||
var body = this.renderActionHeader(n);
|
||
var a = n.action;
|
||
|
||
if (a.isActionTypeDef())
|
||
return this.stmt(n, body)
|
||
|
||
if (a.isPage()) {
|
||
var inner = ""
|
||
|
||
if (a.modelParameter) {
|
||
var k = a.modelParameter.getKind()
|
||
if (k instanceof AST.RecordEntryKind) {
|
||
var r = (<AST.RecordEntryKind>k).getRecord()
|
||
inner +=
|
||
this.tline(this.kw("data")) +
|
||
(r.values.stmts.length == 0 ? this.addModelFieldHint() : this.renderBlock(r.values))
|
||
}
|
||
}
|
||
|
||
inner +=
|
||
this.tline(this.kw("initialize")) +
|
||
this.renderBlock(a.getPageBlock(true)) +
|
||
this.tline(this.kw("display")) +
|
||
this.renderBlock(a.getPageBlock(false));
|
||
return this.stmt(n, body + inner + this.endKeyword("page"));
|
||
}
|
||
|
||
if (a.hasOutParameters() || a.hasInParameters())
|
||
body += this.tline(AST.proMode ? this.st("{") : this.kw("do"));
|
||
|
||
return this.stmt(n, body + this.renderBlock(a.body) + this.endKeyword(this.actionKeyword(a)));
|
||
}
|
||
|
||
public visitLibraryRef(n:AST.LibraryRef)
|
||
{
|
||
return this.stmt(n,
|
||
this.tline(this.kw("import") + this.id(n.getName())) +
|
||
this.tline(this.kw(n.isPublished() ? "published" : "local") + this.id(n.getId())) +
|
||
this.possibleError(n) +
|
||
this.renderBlock(n.resolveClauses));
|
||
}
|
||
|
||
public visitRecordDef(n:AST.RecordDef)
|
||
{
|
||
var keys = n.keys.count() > 0
|
||
? (this.tline(this.kw((n.getKeyTerminology() || "key") + "s")) + this.renderBlock(n.keys))
|
||
: "";
|
||
var values = (n.values.count() > 0 && n.getValueTerminology())
|
||
? (this.tline(this.kw(n.getValueTerminology() + "s")) + this.renderBlock(n.values))
|
||
: "";
|
||
|
||
var kw1 = this.stmt(n.recordPersistence, this.tline(this.kw(n.getPersistenceDescription())));
|
||
var spacer = n.getPersistenceDescription().length
|
||
? " "
|
||
: "";
|
||
var kw2 = this.stmt(n.recordKind, this.tline(this.kw(n.getDefTerminology())));
|
||
var kw3 = this.stmt(n.recordNameHolder, this.tline(this.id(n.getCoreName())));
|
||
var header = this.diffLine(n, n.diffAltDecl, n => [kw1, spacer, kw2, " ", kw3])
|
||
return this.stmt(n, this.possibleError(n) + header + keys + values);
|
||
}
|
||
|
||
private diffTwoWords(s:string, alt:string) : string
|
||
{
|
||
if (!this.showDiff || !alt || alt == s) return AST.proMode ? Renderer.tspan("greyed", s) : this.id(s);
|
||
return this.diffWords([this.id(alt), null, null, this.id(s)])
|
||
}
|
||
|
||
private diffLine<T,S>(stmt:T, alt:S, f:(e:T)=>string[]) : string
|
||
{
|
||
var w0 = f(stmt)
|
||
var r = ""
|
||
if (this.showDiff && alt) {
|
||
var w1 = f(<any>alt)
|
||
var w2 = AST.Diff.minimalEditDistance(w1, w0, (a, b) => a == b)
|
||
r = this.diffWords(w2)
|
||
} else {
|
||
if (w0.length == 0) return "";
|
||
r = w0.join("")
|
||
}
|
||
return this.tline(r)
|
||
}
|
||
|
||
public visitRecordField(n:AST.RecordField)
|
||
{
|
||
var comments = n.commentBlock.children()
|
||
.map(c => this.visitComment(<AST.Comment>c))
|
||
.join("");
|
||
|
||
var pref = n.def() && n.def().isModel ? AST.modelSymbol + "\u200A" : ""
|
||
var str = this.diffLine(n, n.diffAltStmt,
|
||
(n) => [
|
||
this.id(pref + n.getName()),
|
||
this.op(":"),
|
||
this.kind(n.dataKind)
|
||
]);
|
||
|
||
// comments rendered outside of the statement, since they are
|
||
// statements themselves
|
||
return comments + this.stmt(n, str + this.possibleError(n));
|
||
}
|
||
|
||
private libName(l:AST.LibraryRef) { return Renderer.tspan("id symbol", AST.libSymbol) + (l ? this.id(l.getName()) : "?") }
|
||
|
||
public visitResolveClause(n:AST.ResolveClause)
|
||
{
|
||
return this.stmt(n,
|
||
this.tline(this.kw("uses") + this.id(n.getName()) + this.kw("bound to") + this.libName(n.defaultLib) + this.kw("with")) +
|
||
this.possibleError(n) +
|
||
this.renderBlock(n.kindBindings) +
|
||
this.renderBlock(n.actionBindings));
|
||
}
|
||
|
||
public visitActionBinding(n:AST.ActionBinding)
|
||
{
|
||
if (this.showDiff && !n.isExplicit) return ""
|
||
return this.stmt(n, this.tline((n.isExplicit ? this.kw("explicit") : "") +
|
||
this.kw("function") + this.id(n.formalName) + this.kw("bound to") +
|
||
this.libName(n.actualLib) + this.id("\u200A\u2192\u00A0" + (n.getActualName()))) +
|
||
this.possibleError(n))
|
||
}
|
||
|
||
public visitKindBinding(n:AST.KindBinding)
|
||
{
|
||
if (this.showDiff && !n.isExplicit) return ""
|
||
return this.stmt(n, this.tline( /*(n.isExplicit ? this.kw("explicit") : "") + */
|
||
this.kw("type") + this.id(n.formalName) + this.kw("bound to") + this.kind(n.actual)) +
|
||
this.possibleError(n))
|
||
}
|
||
|
||
public visitAction(n:AST.Action)
|
||
{
|
||
return this.dispatch(n.header);
|
||
}
|
||
|
||
public visitGlobalDef(n:AST.GlobalDef)
|
||
{
|
||
var l0 = this.diffLine(n, n.diffAltDecl,
|
||
n => [this.kw(n.isResource ? "art" : "data"), this.id(n.getName()), this.op(":"), this.kind(n.getKind())])
|
||
var l1 = this.diffLine(n, n.diffAltDecl,
|
||
n => n.isResource && n.url ? [this.kw("with"), this.id("data: "), this.renderArtUrl(n.getKind(), n.url)] : [])
|
||
return this.stmt(n, l0 + l1) + this.possibleError(n);
|
||
}
|
||
|
||
public visitDecl(n: AST.Decl)
|
||
{
|
||
Util.check(false);
|
||
return this.tline("Displaying " + n.getName() + " not implemented.");
|
||
}
|
||
|
||
public renderDiff(n: AST.App, showAll = false)
|
||
{
|
||
this.showDiff = true
|
||
|
||
var th = n.things.concat(n.diffRemovedThings || [])
|
||
AST.App.orderThings(th)
|
||
var r = ""
|
||
th.forEach(t => {
|
||
var show = showAll || t.diffStatus
|
||
var prevNum = this.numDiffs
|
||
var str = this.dispatch(t)
|
||
if (prevNum != this.numDiffs) show = true
|
||
if (show)
|
||
r += Renderer.tdiv("decl " + (t.diffStatus < 0 ? "diffDeclRemoved" : t.diffStatus > 0 ? "diffDeclAdded" : ""),
|
||
str)
|
||
})
|
||
return r
|
||
}
|
||
|
||
public visitApp(n: AST.App)
|
||
{
|
||
var r = ""
|
||
n.orderedThings().forEach((t) => {
|
||
r += Renderer.tdiv("decl", this.dispatch(t));
|
||
});
|
||
return r;
|
||
}
|
||
|
||
public renderPropertySig(prop:IProperty, withLinks = false, withKind = true, withKeyword = true)
|
||
{
|
||
var inParms = "";
|
||
var returns = "";
|
||
var outParms = "";
|
||
|
||
var topicLink = (name:string, topic = name) => {
|
||
if (!withLinks) return this.id(name);
|
||
return "<a href='#topic:" + MdComments.shrink(topic) + "'>" + this.id(name) + "</a>";
|
||
}
|
||
|
||
var kindLink = (k:Kind) => {
|
||
//if (!withLinks) return this.kind(k);
|
||
if (!withLinks) return this.id(k.getName());
|
||
return "<a href='#topic:" + MdComments.shrink(k.getName()) + "'>" + this.kind(k) + "</a>";
|
||
}
|
||
|
||
if ((<any>prop)._extensionAction) prop = (<any>prop)._extensionAction;
|
||
var propi = <IPropertyWithNamespaces>prop;
|
||
var isExtension = propi.isExtensionAction && propi.isExtensionAction();
|
||
var params = prop.getParameters();
|
||
var hd = "";
|
||
if (withKeyword)
|
||
hd += this.kw("function ");
|
||
if (withKind) {
|
||
if (isExtension) {
|
||
var __this = params[0];
|
||
hd += kindLink(__this.getKind());
|
||
} else {
|
||
if (prop.parentKind.singleton)
|
||
hd += topicLink(prop.parentKind.singleton.getName())
|
||
else {
|
||
var ns: string = (<IPropertyWithNamespaces>prop).getNamespaces ? (<IPropertyWithNamespaces>prop).getNamespaces()[0] : undefined;
|
||
this.id
|
||
if (ns) hd += this.name(ns);
|
||
else hd += "(" + kindLink(prop.parentKind) + ")";
|
||
}
|
||
}
|
||
params.shift();
|
||
hd += "\u200A\u2192\u00A0";
|
||
}
|
||
hd += this.name(prop.getName())
|
||
|
||
var parms = params.map((p) =>
|
||
(withLinks ? "<br/>" : "") +
|
||
"<span class='sig-parameter'>" + this.id(p.getName()) + "</span>" + this.op(":") + kindLink(p.getKind()))
|
||
|
||
if(parms.length > 0)
|
||
hd += "(" + parms.join(", ") + ")"
|
||
|
||
var rt = prop.getResult().getKind();
|
||
if (rt != api.core.Nothing) {
|
||
hd += (withLinks ? "<br/>" : "");
|
||
hd += this.kw("returns") + kindLink(rt);
|
||
}
|
||
|
||
return Renderer.tdiv("signature", hd);
|
||
}
|
||
}
|
||
|
||
export class CopyRenderer
|
||
extends Renderer
|
||
{
|
||
static css = "<style>\n" +
|
||
"@page { margin: 1in; }\n" +
|
||
".md-tutorial { font-size: 1.0em; line-height: 1.4em; }\n"+
|
||
"@media print { .md-tutorial a:link:after, .md-tutorial a:visited:after { content:\" (\" attr(href) \") \"; } }\n"+
|
||
".parse-error, .decl { margin-bottom: 1em; }\n" +
|
||
".parse-error, .error { color: #d00; }\n" +
|
||
".hint { color: #444; }\n" +
|
||
".errorNumber { color: #aaa; }\n" +
|
||
".kw, .keyword { font-weight: bold; color: black; }\n" +
|
||
".kbm { display:inline-block;} .kbm svg { height: 1em; vertical-align: middle; margin-left: 0.2em; }\n" +
|
||
".kbm svg > path.biton { fill:#000; } .kbm svg > path.bitoff { fill:#ccc; }\n" +
|
||
".code-highlight { border: 1px dashed gray; font-weight:bold; }\n" +
|
||
".comment { color:#444; font-style:italic; }\n" +
|
||
".greyed { color:#444; }\n" +
|
||
".api-kind { border: 1px dotted #BBB; padding: 0.4em; clear: both; font-size: 1.2em; margin-bottom: 0.6em; }\n" +
|
||
".md-snippet { border: 1px dotted #bbb; padding: 0.4em 0; clear: both; line-height: 1.3em; page-break-inside:avoid; }\n" +
|
||
".md-snippet .signature { padding:0em 0.2em 0em 0.2em; }\n" +
|
||
".md-snippet .name { font-weight: bold;}\n" +
|
||
".md-img { margin:0.5em; clear:both; width:100%; text-align:center; position:relative; }\n" +
|
||
".md-img-inner { position:relative; display:inline-block; width:100%; }\n" +
|
||
".md-img .caption { font-size:0.8em; }\n" +
|
||
".md-img img { max-height: 100%; max-width: 100%; }\n" +
|
||
".md-box { page-break-inside: avoid; }\n" +
|
||
".md-box-header, .md-box-header-print { font-weight: bold; font-size: 1.2em; }\n" +
|
||
".md-box, .md-box-landscape, .md-box-portrait { margin-left: 2em; margin-bottom:0.5em; border: 1px solid #555; border-left-width: 0.5em; padding: 1em; }\n" +
|
||
".md-box-avatar { margin-bottom:0.5em; } .md-box-avatar-img { width:4em; display:inline-block; vertical-align:top;} .phone .md-box-avatar-img { width:3em; } .md-box-avatar-body { position:relative; padding:0em 0.5em 0em 0.5em; color:#000; background:#eeeeee; margin-left:1.5em; display:inline-block; width: calc(100% - 7em); } .phone .md-box-avatar-body { width: calc(100% - 6em); } .md-box-avatar-body:after { top: 1.1em; left: -1.5em; bottom: auto; border-width: 0px 1.5em 0.7em 0; border-color: transparent #eeeeee; content: ''; position: absolute; border-style: solid; display: block; width: 0; }\n"+
|
||
".md-box-screen { display:none; }\n" +
|
||
".md-box-card { border: solid 1px black; background: inherit; margin-left:inherit; min-height:4em;}\n" +
|
||
"a { color:black; }\n" +
|
||
"a.md-api-entry-link, .api-kind a { text-decoration: none; }\n" +
|
||
".api-desc { color: black; font-size: 0.8em; }\n" +
|
||
".signature { font-size: 1.3em; }\n" +
|
||
".md-api-entry { color: black; border: 1px dotted #BBB; padding: 0.6em 1em 0.5em 1em; margin: 0.5em 0; }\n" +
|
||
".md-api-header { font-size: 1.4em; margin-bottom: 1em; }\n" +
|
||
".md-api-header .signature { margin-bottom: 0.4em; }\n" +
|
||
".md-api-header .sig-parameter { display:inline-block; margin-left: 4em; }\n" +
|
||
".md-para { margin-top: 1em; margin-bottom: 1em; }\n"+
|
||
".block .md-para { margin: 0; }\n"+
|
||
"span.stringLiteral { word-break: break-all; } div.stringLiteral { white-space:pre-wrap; }\n" +
|
||
".md-warning { background-color:lightyellow; border-left:solid 0.25em red; padding:0.5em; margin-left:2em;}\n"+
|
||
"div.md-video-link { position: relative; } div.md-video-link > img { width: calc(100 % - 1em); } div.md-video-link > svg { position: absolute;left:0em; bottom: 0em; width:25%; } @media print { div.md-video-link > svg { display:none; } }\n" +
|
||
"@media print { a.md-external-link:link:after, a.md-external-link:visited:after { content: ' (' attr(href) ') '; font-size: 80%; } }\n"+
|
||
"svg.video-play { width: calc(100% - 1em); background-size:cover; background-repeat:no-repeat;}\n"+
|
||
".nopara .md-para { margin:0; }\n"+
|
||
'.symbol { font-family: "Segoe UI Symbol", inherit; }\n' +
|
||
".placeholder { font-size: 0.7em; padding: 0.2em; border: 1px dotted gray; }\n" +
|
||
".md-comment, .md-comment h1, .md-comment h2, .md-comment h3, .md-comment h4 { display: inline-block; margin:0; }\n" +
|
||
"code { font-size: 1.0em; font-family: Calibri, \"Helvetica Neue\", HelveticaNeue, Helvetica, Arial, sans-serif; border: 1px dotted #bbb; padding: 0em 0.2em 0.1em 0.2em; }\n" +
|
||
"code.md-ui { border: 2px solid #777; padding-left:0.2em; padding-right:0.2em; }\n" +
|
||
".block { margin-left: 1em; }\n" +
|
||
".stmt { border-left: 0.2em solid #aaa; margin-top: 0.2em; }\n" +
|
||
".line { margin-left: 0.4em; }\n" +
|
||
".tutorial-step { font-size: 1.7em; }\n" +
|
||
".tutorial-step .md-comment { font-size: 0.6em; }\n" +
|
||
".decl > .stmt { border: none; }\n" +
|
||
".print-big { font-size: 1.5em; }\n" +
|
||
"a.md-bigbutton {}\n" +
|
||
"@media print { #MicrosoftTranslatorWidget { display: none; } }\n" +
|
||
'body { font-family: Calibri, "Helvetica Neue", HelveticaNeue, Helvetica, Arial, sans-serif; }\n' +
|
||
"</style>";
|
||
|
||
//".stmt-comment { border: none; }\n" +
|
||
//".stmt-comment > .line { margin-left: 0; }\n" +
|
||
|
||
constructor()
|
||
{
|
||
super();
|
||
this.stringLimit = 1000;
|
||
this.formatComments = false;
|
||
}
|
||
|
||
public tline(s:string) {
|
||
return Renderer.tdiv("line", s.trim());
|
||
}
|
||
|
||
public kw(s:string) {
|
||
return " <span class='keyword'>" + Util.htmlEscape(s) + "</span> ";
|
||
}
|
||
|
||
public message(cls:string, s:string)
|
||
{
|
||
if (/error/.test(cls))
|
||
return "<div class='error'>" + s.trim() + "</div>";
|
||
else
|
||
return "<div class='hint'>" + s.trim() + "</div>";
|
||
}
|
||
|
||
public visitActionBinding(n:AST.ActionBinding)
|
||
{
|
||
if (!n.isExplicit && !n.getError()) return "";
|
||
return super.visitActionBinding(n);
|
||
}
|
||
|
||
public visitKindBinding(n:AST.KindBinding)
|
||
{
|
||
if (!n.isExplicit && !n.getError()) return "";
|
||
return super.visitKindBinding(n);
|
||
}
|
||
|
||
static styleMap = {
|
||
'<span class=.keyword': "font-weight: bold; color: #41900D",
|
||
'<div class=.block': "margin-left: 1em",
|
||
'<div class=.md-snippet': "border: 1px dotted #bbb; padding: 0.5em; margin-left: 2em",
|
||
'<div class=.md-para': "margin: 1em 0",
|
||
'<div class=.md-img': "margin: 0.5em; text-align: center",
|
||
}
|
||
|
||
static inlineStyles(text:string):string
|
||
{
|
||
var bodyStyle =
|
||
"font-family: 'Segoe UI', 'Helvetica', sans-serif;" +
|
||
"font-size:16px;" +
|
||
//"max-width: 600px;" +
|
||
//"margin: 1em auto;" +
|
||
"margin: 0.5em;" +
|
||
"";
|
||
|
||
Object.keys(CopyRenderer.styleMap).forEach(s => {
|
||
var inl = CopyRenderer.styleMap[s]
|
||
var ss = Util.fmt(" style='{0:q}'", inl)
|
||
text = text.replace(new RegExp(s + "[\"']", "g"), (m) => m + ss)
|
||
})
|
||
|
||
return Util.fmt("<div class='column' style='{0:q}'><!--HEADER-->{1}<!--FOOTER--></div>", bodyStyle, text)
|
||
}
|
||
}
|
||
}
|