Esempio n. 1
0
        /// <summary>
        /// Wraps the symbolic part of the operand with HTML link notation.  If the operand
        /// doesn't have a linkable symbol, this return null.
        /// </summary>
        /// <remarks>
        /// We're playing games with string substitution that feel a little flimsy, but this
        /// is much simpler than reformatting the operand from scratch.
        /// </remarks>
        /// <param name="index">Display line index.</param>
        private string GetLinkOperand(int index, string operand)
        {
            LineListGen.Line line = mCodeLineList[index];
            if (line.FileOffset < 0)
            {
                // EQU directive - shouldn't be here
                Debug.Assert(false);
                return(null);
            }

            // Check for a format descriptor with a symbol.
            Debug.Assert(line.LineType == LineListGen.Line.Type.Code ||
                         line.LineType == LineListGen.Line.Type.Data);
            Anattrib attr = mProject.GetAnattrib(line.FileOffset);

            if (attr.DataDescriptor == null || !attr.DataDescriptor.HasSymbol)
            {
                return(null);
            }

            // Symbol refs are weak.  If the symbol doesn't exist, the value will be
            // formatted in hex.  We can't simply check to see if the formatted operand
            // contains the symbol, because we could false-positive on the use of symbols
            // whose label is a valid hex value, e.g. "ABCD = $ABCD".
            //
            // We also want to exclude references to local variables, since those aren't
            // unique.  To handle local refs we could just create anchors by line number or
            // some other means of unique identification.
            if (!mProject.SymbolTable.TryGetNonVariableValue(attr.DataDescriptor.SymbolRef.Label,
                                                             out Symbol sym))
            {
                return(null);
            }

            string linkified = "<a href=\"#" + LABEL_LINK_PREFIX + sym.Label + "\">" +
                               sym.Label + "</a>";

            return(TextUtil.EscapeHTML(operand).Replace(sym.Label, linkified));
        }
Esempio n. 2
0
        /// <summary>
        /// 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
        /// every line as a table row, with HTML column definitions for each of our columns.
        /// </remarks>
        /// <param name="index">Index of line to output.</param>
        /// <param name="tw">Text output destination.</param>
        /// <param name="sb">String builder to append text to.  Must be cleared before
        ///   calling here.  (This is a minor optimization.)</param>
        private void GenerateHtmlLine(int index, TextWriter tw, StringBuilder sb)
        {
            LineListGen.Line line = mCodeLineList[index];
            if (line.LineType == LineListGen.Line.Type.Note && !IncludeNotes)
            {
                return;
            }

            sb.Clear();

            // Width of "bytes" field, without '+' or trailing space.
            int bytesWidth = mColStart[(int)Col.Bytes + 1] - mColStart[(int)Col.Bytes] - 2;
            // Width of "label" field, without trailing space.
            int maxLabelLen = mColStart[(int)Col.Label + 1] - mColStart[(int)Col.Label] - 1;

            DisplayList.FormattedParts parts = mCodeLineList.GetFormattedParts(index);

            // If needed, create an HTML anchor for the label field.
            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))
            {
                if (parts.Label.StartsWith(mFormatter.NonUniqueLabelPrefix))
                {
                    // TODO(someday): handle non-unique labels.  ':' is valid in HTML anchors,
                    // so we can use that to distinguish them from other labels, but we still
                    // need to ensure that the label is unique and all references point to the
                    // correct instance.  We can't get that from the Parts list.
                }
                else
                {
                    anchorLabel = "<a name=\"" + LABEL_LINK_PREFIX + parts.Label +
                                  "\">" + parts.Label + "</a>";
                }
            }

            // If needed, create an HTML link for the operand field.
            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);
            }

            // Put long labels on their own line if desired.
            bool suppressLabel = false;

            if (LongLabelNewLine && (line.LineType == LineListGen.Line.Type.Code ||
                                     line.LineType == LineListGen.Line.Type.Data))
            {
                int labelLen = parts.Label.Length;
                if (labelLen > maxLabelLen)
                {
                    // put on its own line
                    string lstr;
                    if (anchorLabel != null)
                    {
                        lstr = anchorLabel;
                    }
                    else
                    {
                        lstr = parts.Label;
                    }
                    AddSpacedString(sb, 0, mColStart[(int)Col.Label], lstr, parts.Label.Length);
                    tw.WriteLine(sb);
                    sb.Clear();
                    suppressLabel = true;
                }
            }

            int colPos = 0;

            switch (line.LineType)
            {
            case LineListGen.Line.Type.Code:
            case LineListGen.Line.Type.Data:
            case LineListGen.Line.Type.EquDirective:
            case LineListGen.Line.Type.RegWidthDirective:
            case LineListGen.Line.Type.OrgDirective:
            case LineListGen.Line.Type.LocalVariableTable:
                if (parts.IsLongComment)
                {
                    // This happens for long comments embedded in LV tables, e.g.
                    // "clear table".
                    AddSpacedString(sb, 0, mColStart[(int)Col.Label],
                                    TextUtil.EscapeHTML(parts.Comment), parts.Comment.Length);
                    break;
                }

                // these columns are optional

                if ((mLeftFlags & ActiveColumnFlags.Offset) != 0)
                {
                    colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Offset],
                                             parts.Offset, parts.Offset.Length);
                }
                if ((mLeftFlags & ActiveColumnFlags.Address) != 0)
                {
                    if (!string.IsNullOrEmpty(parts.Addr))
                    {
                        string str = parts.Addr + ":";
                        colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Address],
                                                 str, str.Length);
                    }
                }
                if ((mLeftFlags & ActiveColumnFlags.Bytes) != 0)
                {
                    // Shorten the "...".
                    string bytesStr = parts.Bytes;
                    if (bytesStr != null)
                    {
                        if (bytesStr.Length > bytesWidth)
                        {
                            bytesStr = bytesStr.Substring(0, bytesWidth) + "+";
                        }
                        colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Bytes],
                                                 bytesStr, bytesStr.Length);
                    }
                }
                if ((mLeftFlags & ActiveColumnFlags.Flags) != 0)
                {
                    colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Flags],
                                             parts.Flags, parts.Flags.Length);
                }
                if ((mLeftFlags & ActiveColumnFlags.Attr) != 0)
                {
                    colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Attr],
                                             TextUtil.EscapeHTML(parts.Attr), parts.Attr.Length);
                }

                // remaining columns are mandatory, but may be empty

                if (suppressLabel)
                {
                    // label on previous line
                }
                else if (anchorLabel != null)
                {
                    colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Label],
                                             anchorLabel, parts.Label.Length);
                }
                else if (parts.Label != null)
                {
                    colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Label],
                                             parts.Label, parts.Label.Length);
                }

                colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Opcode],
                                         parts.Opcode, parts.Opcode.Length);

                if (linkOperand != null)
                {
                    colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Operand],
                                             linkOperand, parts.Operand.Length);
                }
                else
                {
                    colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Operand],
                                             TextUtil.EscapeHTML(parts.Operand), parts.Operand.Length);
                }

                if (parts.Comment != null)
                {
                    colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Comment],
                                             TextUtil.EscapeHTML(parts.Comment), parts.Comment.Length);
                }
                break;

            case LineListGen.Line.Type.LongComment:
            case LineListGen.Line.Type.Note:
                // Notes have a background color.  Use this to highlight the text.  We
                // don't apply it to the padding on the left columns.
                int rgb = 0;
                if (parts.HasBackgroundColor)
                {
                    SolidColorBrush b = parts.BackgroundBrush as SolidColorBrush;
                    if (b != null)
                    {
                        rgb = (b.Color.R << 16) | (b.Color.G << 8) | (b.Color.B);
                    }
                }
                string cstr;
                if (rgb != 0)
                {
                    cstr = string.Format("<span style=\"background-color: #{0:x6}\">{1}</span>",
                                         rgb, TextUtil.EscapeHTML(parts.Comment));
                }
                else
                {
                    cstr = TextUtil.EscapeHTML(parts.Comment);
                }
                colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Label], cstr,
                                         parts.Comment.Length);
                break;

            case LineListGen.Line.Type.VisualizationSet:
                if (!GenerateImageFiles)
                {
                    // generate nothing at all
                    return;
                }
                while (colPos < mColStart[(int)Col.Label])
                {
                    sb.Append(' ');
                    colPos++;
                }
                OutputVisualizationSet(line.FileOffset, sb);
                break;

            case LineListGen.Line.Type.Blank:
                break;

            default:
                Debug.Assert(false);
                break;
            }

            tw.WriteLine(sb);
        }