mirror of
https://github.com/fadden/6502bench.git
synced 2025-01-13 15:33:02 +00:00
Fix HTML output
We weren't escaping '<', '>', and '&', which caused browsers to get very confused. Browsers seem to prefer <PRE> to <CODE> for long blocks of text, so switch to that. Also, added support for putting long labels on their own lines in the HTML output. Also, fixed some unescaped angle brackets in the manual. Also, tweaked the edit instruction operand a bit more.
This commit is contained in:
parent
1ddf4bed48
commit
d01c61fbc1
@ -199,6 +199,27 @@ namespace CommonUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Escapes a string for HTML.
|
||||
/// </summary>
|
||||
/// <param name="str">String to process.</param>
|
||||
/// <returns>Escaped string.</returns>
|
||||
public static string EscapeHTML(string str) {
|
||||
StringBuilder sb = new StringBuilder(str.Length);
|
||||
for (int i = 0; i < str.Length; i++) {
|
||||
if (str[i] == '<') {
|
||||
sb.Append("<");
|
||||
} else if (str[i] == '>') {
|
||||
sb.Append(">");
|
||||
} else if (str[i] == '&') {
|
||||
sb.Append("&");
|
||||
} else {
|
||||
sb.Append(str[i]);
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes an integer array into a string.
|
||||
/// </summary>
|
||||
|
@ -96,6 +96,11 @@ namespace SourceGen {
|
||||
COUNT // number of elements, must be last
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If set, labels that are wider than the label column should go on their own line.
|
||||
/// </summary>
|
||||
private bool mLongLabelNewLine;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
@ -107,6 +112,10 @@ namespace SourceGen {
|
||||
mFormatter = formatter;
|
||||
mLeftFlags = leftFlags;
|
||||
|
||||
// Go ahead and latch this here.
|
||||
mLongLabelNewLine =
|
||||
AppSettings.Global.GetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, false);
|
||||
|
||||
ConfigureColumns(leftFlags, rightWidths);
|
||||
}
|
||||
|
||||
@ -399,7 +408,8 @@ namespace SourceGen {
|
||||
|
||||
// With the style "code { white-space: pre; }", leading spaces and EOL markers
|
||||
// are preserved.
|
||||
sw.Write("<code style=\"white-space: pre;\">");
|
||||
//sw.Write("<code style=\"white-space: pre;\">");
|
||||
sw.Write("<pre>");
|
||||
StringBuilder sb = new StringBuilder(128);
|
||||
for (int lineIndex = 0; lineIndex < mCodeLineList.Count; lineIndex++) {
|
||||
if (Selection != null && !Selection[lineIndex]) {
|
||||
@ -407,12 +417,12 @@ namespace SourceGen {
|
||||
}
|
||||
|
||||
if (GenerateHtmlLine(lineIndex, sb)) {
|
||||
sw.WriteLine(sb.ToString());
|
||||
//sw.WriteLine("<br/>");
|
||||
sw.Write(sb.ToString());
|
||||
}
|
||||
sb.Clear();
|
||||
}
|
||||
sw.WriteLine("</code>\r\n");
|
||||
//sw.WriteLine("</code>\r\n");
|
||||
sw.WriteLine("</pre>\r\n");
|
||||
|
||||
sw.Write(template2);
|
||||
}
|
||||
@ -433,8 +443,11 @@ namespace SourceGen {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a line of HTML output. The line will not have EOL markers added.
|
||||
/// Generates HTML output for one display line. This may result in more than one line
|
||||
/// of HTML output, e.g. if the label is longer than the field. EOL markers will
|
||||
/// be added.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Currently just generating a line of pre-formatted text. We could also output
|
||||
@ -452,7 +465,43 @@ namespace SourceGen {
|
||||
LineListGen.Line line = mCodeLineList[index];
|
||||
DisplayList.FormattedParts parts = mCodeLineList.GetFormattedParts(index);
|
||||
|
||||
// TODO: linkify label and operand fields
|
||||
int maxLabelLen = mColEnd[(int)Col.Label] - mColEnd[(int)Col.Attr] - 1;
|
||||
|
||||
string anchorLabel = null;
|
||||
if ((line.LineType == LineListGen.Line.Type.Code ||
|
||||
line.LineType == LineListGen.Line.Type.Data ||
|
||||
line.LineType == LineListGen.Line.Type.EquDirective) &&
|
||||
!string.IsNullOrEmpty(parts.Label)) {
|
||||
anchorLabel = "<a name=\"" + LABEL_LINK_PREFIX + parts.Label +
|
||||
"\">" + parts.Label + "</a>";
|
||||
}
|
||||
|
||||
string linkOperand = null;
|
||||
if ((line.LineType == LineListGen.Line.Type.Code ||
|
||||
line.LineType == LineListGen.Line.Type.Data) &&
|
||||
parts.Operand.Length > 0) {
|
||||
linkOperand = GetLinkOperand(index, parts.Operand);
|
||||
}
|
||||
|
||||
int colPos = 0;
|
||||
|
||||
bool suppressLabel = false;
|
||||
if (mLongLabelNewLine && (line.LineType == LineListGen.Line.Type.Code ||
|
||||
line.LineType == LineListGen.Line.Type.Data)) {
|
||||
int labelLen = string.IsNullOrEmpty(parts.Label) ? 0 : parts.Label.Length;
|
||||
if (labelLen > maxLabelLen) {
|
||||
// put on its own line
|
||||
AddSpacedString(sb, colPos, mColEnd[(int)Col.Attr], string.Empty, 0);
|
||||
if (anchorLabel != null) {
|
||||
sb.Append(anchorLabel);
|
||||
} else {
|
||||
sb.Append(parts.Label);
|
||||
}
|
||||
sb.Append("\r\n");
|
||||
suppressLabel = true;
|
||||
Debug.Assert(colPos == 0);
|
||||
}
|
||||
}
|
||||
|
||||
switch (line.LineType) {
|
||||
case LineListGen.Line.Type.Code:
|
||||
@ -462,17 +511,19 @@ namespace SourceGen {
|
||||
case LineListGen.Line.Type.OrgDirective:
|
||||
case LineListGen.Line.Type.LocalVariableTable:
|
||||
if (parts.IsLongComment) {
|
||||
// This happens for long comments embedded in LV tables.
|
||||
if (mColEnd[(int)Col.Attr] != 0) {
|
||||
TextUtil.AppendPaddedString(sb, string.Empty, mColEnd[(int)Col.Attr]);
|
||||
}
|
||||
sb.Append(parts.Comment);
|
||||
// This happens for long comments embedded in LV tables, e.g.
|
||||
// "clear table".
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Attr],
|
||||
string.Empty, 0);
|
||||
sb.Append(TextUtil.EscapeHTML(parts.Comment));
|
||||
break;
|
||||
}
|
||||
|
||||
// these columns are optional
|
||||
|
||||
if ((mLeftFlags & ActiveColumnFlags.Offset) != 0) {
|
||||
TextUtil.AppendPaddedString(sb, parts.Offset,
|
||||
mColEnd[(int)Col.Offset]);
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Offset],
|
||||
parts.Offset, parts.Offset.Length);
|
||||
}
|
||||
if ((mLeftFlags & ActiveColumnFlags.Address) != 0) {
|
||||
string str;
|
||||
@ -481,71 +532,72 @@ namespace SourceGen {
|
||||
} else {
|
||||
str = string.Empty;
|
||||
}
|
||||
TextUtil.AppendPaddedString(sb, str, mColEnd[(int)Col.Address]);
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Address],
|
||||
str, str.Length);
|
||||
}
|
||||
if ((mLeftFlags & ActiveColumnFlags.Bytes) != 0) {
|
||||
// Shorten the "...".
|
||||
string bytesStr = parts.Bytes;
|
||||
if (bytesStr != null && bytesStr.Length > bytesWidth) {
|
||||
if (bytesStr == null) {
|
||||
bytesStr = string.Empty;
|
||||
}
|
||||
if (bytesStr.Length > bytesWidth) {
|
||||
bytesStr = bytesStr.Substring(0, bytesWidth) + "+";
|
||||
}
|
||||
TextUtil.AppendPaddedString(sb, bytesStr, mColEnd[(int)Col.Bytes]);
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Bytes],
|
||||
bytesStr, bytesStr.Length);
|
||||
}
|
||||
if ((mLeftFlags & ActiveColumnFlags.Flags) != 0) {
|
||||
TextUtil.AppendPaddedString(sb, parts.Flags, mColEnd[(int)Col.Flags]);
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Flags],
|
||||
parts.Flags, parts.Flags.Length);
|
||||
}
|
||||
if ((mLeftFlags & ActiveColumnFlags.Attr) != 0) {
|
||||
TextUtil.AppendPaddedString(sb, parts.Attr, mColEnd[(int)Col.Attr]);
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Attr],
|
||||
TextUtil.EscapeHTML(parts.Attr), parts.Attr.Length);
|
||||
}
|
||||
int labelOffset = sb.Length;
|
||||
TextUtil.AppendPaddedString(sb, parts.Label, mColEnd[(int)Col.Label]);
|
||||
TextUtil.AppendPaddedString(sb, parts.Opcode, mColEnd[(int)Col.Opcode]);
|
||||
int operandOffset = sb.Length;
|
||||
TextUtil.AppendPaddedString(sb, parts.Operand, mColEnd[(int)Col.Operand]);
|
||||
|
||||
// remaining columns are mandatory, but may be empty
|
||||
|
||||
if (suppressLabel) {
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Label],
|
||||
string.Empty, 0);
|
||||
} else if (anchorLabel != null) {
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Label],
|
||||
anchorLabel, parts.Label.Length);
|
||||
} else if (parts.Label != null) {
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Label],
|
||||
parts.Label, parts.Label.Length);
|
||||
}
|
||||
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Opcode],
|
||||
parts.Opcode, parts.Opcode.Length);
|
||||
|
||||
if (linkOperand != null) {
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Operand],
|
||||
linkOperand, parts.Operand.Length);
|
||||
} else {
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Operand],
|
||||
TextUtil.EscapeHTML(parts.Operand), parts.Operand.Length);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(parts.Comment)) {
|
||||
// Trim trailing spaces off opcode or operand. Would be more efficient
|
||||
// to just not generate the spaces, but this is simpler and we're not
|
||||
// in a hurry.
|
||||
TextUtil.TrimEnd(sb);
|
||||
} else {
|
||||
sb.Append(parts.Comment);
|
||||
sb.Append(TextUtil.EscapeHTML(parts.Comment));
|
||||
}
|
||||
|
||||
// Replace label with anchor label. We do it this late because we need the
|
||||
// spacing to be properly set, and I don't feel like changing how all the
|
||||
// AppendPaddedString code works.
|
||||
if ((line.LineType == LineListGen.Line.Type.Code ||
|
||||
line.LineType == LineListGen.Line.Type.Data ||
|
||||
line.LineType == LineListGen.Line.Type.EquDirective) &&
|
||||
!string.IsNullOrEmpty(parts.Label)) {
|
||||
string linkLabel = "<a name=\"" + LABEL_LINK_PREFIX + parts.Label +
|
||||
"\">" + parts.Label + "</a>";
|
||||
sb.Remove(labelOffset, parts.Label.Length);
|
||||
sb.Insert(labelOffset, linkLabel);
|
||||
|
||||
// Adjust operand position.
|
||||
operandOffset += linkLabel.Length - parts.Label.Length;
|
||||
}
|
||||
|
||||
if ((line.LineType == LineListGen.Line.Type.Code ||
|
||||
line.LineType == LineListGen.Line.Type.Data) &&
|
||||
parts.Operand.Length > 0) {
|
||||
string linkOperand = GetLinkOperand(index, parts.Operand);
|
||||
if (!string.IsNullOrEmpty(linkOperand)) {
|
||||
sb.Remove(operandOffset, parts.Operand.Length);
|
||||
sb.Insert(operandOffset, linkOperand);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LineListGen.Line.Type.LongComment:
|
||||
case LineListGen.Line.Type.Note:
|
||||
if (line.LineType == LineListGen.Line.Type.Note && !IncludeNotes) {
|
||||
return false;
|
||||
}
|
||||
if (mColEnd[(int)Col.Attr] != 0) {
|
||||
// Long comments aren't the left-most field, so pad it out.
|
||||
TextUtil.AppendPaddedString(sb, string.Empty, mColEnd[(int)Col.Attr]);
|
||||
}
|
||||
// Long comments aren't the left-most field, so pad it out.
|
||||
colPos = AddSpacedString(sb, colPos, mColEnd[(int)Col.Attr],
|
||||
string.Empty, 0);
|
||||
|
||||
// Notes have a background color. Use this to highlight the text. We
|
||||
// don't apply it to the padding on the left columns.
|
||||
@ -558,10 +610,10 @@ namespace SourceGen {
|
||||
}
|
||||
if (rgb != 0) {
|
||||
sb.AppendFormat("<span style=\"background-color: #{0:x6}\">", rgb);
|
||||
sb.Append(parts.Comment);
|
||||
sb.Append(TextUtil.EscapeHTML(parts.Comment));
|
||||
sb.Append("</span>");
|
||||
} else {
|
||||
sb.Append(parts.Comment);
|
||||
sb.Append(TextUtil.EscapeHTML(parts.Comment));
|
||||
}
|
||||
break;
|
||||
case LineListGen.Line.Type.Blank:
|
||||
@ -570,9 +622,33 @@ namespace SourceGen {
|
||||
Debug.Assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
sb.Append("\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
private int AddSpacedString(StringBuilder sb, int startPosn, int colEnd, string str,
|
||||
int virtualLength) {
|
||||
int newLen = startPosn + virtualLength;
|
||||
if (startPosn + virtualLength >= colEnd) {
|
||||
// Off end of column. Output string plus one space, unless the length is zero
|
||||
// (because that means we're just padding to the start of a column).
|
||||
sb.Append(str);
|
||||
if (virtualLength != 0) {
|
||||
sb.Append(' ');
|
||||
}
|
||||
return newLen + 1;
|
||||
} else {
|
||||
// Short of column end. Add spaces until we reach it.
|
||||
sb.Append(str);
|
||||
while (newLen < colEnd) {
|
||||
sb.Append(' ');
|
||||
newLen++;
|
||||
}
|
||||
}
|
||||
return newLen;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wraps the symbolic part of the operand with HTML link notation. If the operand
|
||||
/// doesn't have a linkable symbol, this return null.
|
||||
@ -613,7 +689,7 @@ namespace SourceGen {
|
||||
|
||||
string linkified = "<a href=#" + LABEL_LINK_PREFIX + sym.Label + ">" +
|
||||
sym.Label + "</a>";
|
||||
return operand.Replace(sym.Label, linkified);
|
||||
return TextUtil.EscapeHTML(operand).Replace(sym.Label, linkified);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -118,8 +118,8 @@ so any failure is an opportunity for improvement.</p>
|
||||
SourceGen works around when generating code.</p>
|
||||
<p>Every assembler seems to have a different way of dealing with expressions.
|
||||
Most of them will let you group expressions with parenthesis, but that
|
||||
doesn't always help. For example, <code>PEA label >> 8 + 1</code> is
|
||||
perfectly valid, but writing <code>PEA (label >> 8) + 1</code> will cause
|
||||
doesn't always help. For example, <code>PEA label >> 8 + 1</code> is
|
||||
perfectly valid, but writing <code>PEA (label >> 8) + 1</code> will cause
|
||||
most assemblers to assume you're trying to use an alternate (and non-existent)
|
||||
form of <code>PEA</code> with indirect addressing, causing the assembler
|
||||
to halt with an error message. The code generator needs
|
||||
@ -236,11 +236,11 @@ code, but also needs to know how to handle the corner cases.</p>
|
||||
|
||||
<p>Quirks:</p>
|
||||
<ul>
|
||||
<li>Operator precedence is unusual. Consider <code>label >> 8 - 16</code>.
|
||||
<li>Operator precedence is unusual. Consider <code>label >> 8 - 16</code>.
|
||||
cc65 puts shift higher than subtraction, whereas languages like C
|
||||
and assemblers like 64tass do it the other way around. So cc65
|
||||
regards the expression as <code>(label >> 8) - 16</code>, while the
|
||||
more common interpretation would be <code>label >> (8 - 16)</code>.
|
||||
regards the expression as <code>(label >> 8) - 16</code>, while the
|
||||
more common interpretation would be <code>label >> (8 - 16)</code>.
|
||||
(This is actually somewhat convenient, since none of the expressions
|
||||
SourceGen currently generates require parenthesis.)</li>
|
||||
<li>Undocumented opcode <code>SBX</code> ($cb) uses the mnemonic AXS. All
|
||||
@ -308,14 +308,15 @@ code, but also needs to know how to handle the corner cases.</p>
|
||||
<p>The "export" function takes what you see in the code list in the app
|
||||
and converts it to text or HTML. The options you've set in the app
|
||||
settings, such as capitalization, text delimiters, pseudo-opcode names,
|
||||
and operand expression style, are all taken into account. The file
|
||||
generated is not expected to work with an actual assembler.</p>
|
||||
operand expression style, and display of cycle counts are all taken into
|
||||
account. The file generated is not expected to work with an actual
|
||||
assembler.</p>
|
||||
<p>The text output is similar to what you'd get by copying lines to the
|
||||
clipboard and pasting them into a text file, except that you have greater
|
||||
control over which columns are included. The HTML version is augmented
|
||||
with links.</p>
|
||||
|
||||
<p>Use File > Export to open the export dialog. You have several options:</p>
|
||||
<p>Use File > Export to open the export dialog. You have several options:</p>
|
||||
<ul>
|
||||
<li><b>Include only selected lines</b>. This allows you to choose between
|
||||
exporting all or part of a file. If no lines are selected, the entire
|
||||
@ -330,9 +331,7 @@ with links.</p>
|
||||
<li><b>Column widths</b>. These determine the minimum widths of the
|
||||
rightmost four columns. These are not hard limits: if the contents
|
||||
of the column are too wide, the next column will start farther over.
|
||||
The width specified does not include the space character that is
|
||||
automatically added between fields. The widths are not used at
|
||||
all for CSV output.</li>
|
||||
The widths are not used at all for CSV output.</li>
|
||||
<li><b>Text vs. CSV</b>. For text generation, you can choose between
|
||||
plain text and Comma-Separated Value format. The latter is useful
|
||||
for importing source code into another application, such as a
|
||||
|
@ -118,7 +118,7 @@ limitations under the License.
|
||||
IsEnabled="{Binding IsPartPanelEnabled}">
|
||||
<RadioButton GroupName="Part" Content="Low"
|
||||
IsChecked="{Binding FormatPartLow}"/>
|
||||
<RadioButton GroupName="Part" Content="_High" Margin="10,0,0,0"
|
||||
<RadioButton GroupName="Part" Content="H_igh" Margin="10,0,0,0"
|
||||
IsChecked="{Binding FormatPartHigh}"/>
|
||||
<RadioButton GroupName="Part" Content="Bank" Margin="10,0,0,0"
|
||||
IsChecked="{Binding FormatPartBank}"/>
|
||||
|
@ -947,7 +947,7 @@ namespace SourceGen.WpfGui {
|
||||
NarTargetLabel = mEditedLabel.Label;
|
||||
}
|
||||
|
||||
// Sort nice to just hit return twice after entering a label, so move the focus
|
||||
// Sort of nice to just hit return twice after entering a label, so move the focus
|
||||
// to the OK button.
|
||||
okButton.Focus();
|
||||
}
|
||||
@ -985,6 +985,9 @@ namespace SourceGen.WpfGui {
|
||||
// The preview and symbol value display will use mEditedProjectSymbol if it's the
|
||||
// only place the symbol exists, so we want to keep the other controls updated.
|
||||
UpdateControls();
|
||||
|
||||
// Move the focus to the OK button.
|
||||
okButton.Focus();
|
||||
}
|
||||
|
||||
private void CopyToOperandButton_Click(object sender, RoutedEventArgs e) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user