/// <summary> /// Creates an UndoableChange for a label update. /// </summary> /// <param name="offset">Affected offset.</param> /// <param name="oldSymbol">Current label. May be null.</param> /// <param name="newSymbol">New label. May be null.</param> /// <returns>Change record.</returns> public static UndoableChange CreateLabelChange(int offset, Symbol oldSymbol, Symbol newSymbol) { if (oldSymbol == newSymbol) { Debug.WriteLine("No-op label change at +" + offset.ToString("x6") + ": " + oldSymbol); } UndoableChange uc = new UndoableChange(); uc.Type = ChangeType.SetLabel; uc.Offset = offset; uc.OldValue = oldSymbol; uc.NewValue = newSymbol; // Data analysis can change if we add or remove a label in a data area. Label // selection can change as well, e.g. switching from an auto-label to a user // label with an adjustment. So renaming a user-defined label doesn't require // reanalysis, but adding or removing one does. // // Do the reanalysis if either is empty. This will cause an unnecessary // reanalysis if we change an empty label to an empty label, but that shouldn't // be allowed by the UI anyway. Debug.Assert(newSymbol == null || newSymbol.SymbolSource == Symbol.Source.User); if ((oldSymbol == null) || (newSymbol == null) /*|| * (oldSymbol.SymbolSource != newSymbol.SymbolSource)*/) { uc.ReanalysisRequired = ReanalysisScope.DataOnly; } else { uc.ReanalysisRequired = ReanalysisScope.None; } return(uc); }
/// <summary> /// Creates an UndoableChange that does nothing but force an update. /// </summary> /// <param name="flags">Desired reanalysis flags.</param> /// <returns>Change record.</returns> public static UndoableChange CreateDummyChange(ReanalysisScope flags) { UndoableChange uc = new UndoableChange(); uc.Type = ChangeType.Dummy; uc.Offset = -1; uc.ReanalysisRequired = flags; return(uc); }
/// <summary> /// Creates an UndoableChange for an operand or data format update. /// </summary> /// <param name="offset">Affected offset.</param> /// <param name="oldFormat">Current format. May be null.</param> /// <param name="newFormat">New format. May be null.</param> /// <returns>Change record.</returns> public static UndoableChange CreateOperandFormatChange(int offset, FormatDescriptor oldFormat, FormatDescriptor newFormat) { if (oldFormat == newFormat) { Debug.WriteLine("No-op format change at +" + offset.ToString("x6") + ": " + oldFormat); } // We currently allow old/new formats with different lengths. There doesn't // seem to be a reason not to, and a slight performance advantage to doing so. // Also, if a change set has two changes at the same offset, undo requires // enumerating the list in reverse order. UndoableChange uc = new UndoableChange(); uc.Type = ChangeType.SetOperandFormat; uc.Offset = offset; uc.OldValue = oldFormat; uc.NewValue = newFormat; // Data-only reanalysis is required if the old or new format has a label. Simply // changing from e.g. default to decimal, or decimal to binary, doesn't matter. // (The format editing code ensures that labels don't appear in the middle of // a formatted region.) Adding, removing, or changing a symbol can change the // layout of uncategorized data, affect data targets, xrefs, etc. // // We can't only check for a symbol, though, because Numeric/Address will // create an auto-label if the reference is within the file. // // If the number of bytes covered by the format changes, or we're adding or // removing a format, we need to redo the analysis of uncategorized data. For // example, an auto-detected string could get larger or smaller. We don't // currently have a separate flag for just that. Also, because we're focused // on just one change, we can't skip reanalysis when (say) one 4-byte numeric // is converted to two two-byte numerics. if ((oldFormat != null && oldFormat.HasSymbolOrAddress) || (newFormat != null && newFormat.HasSymbolOrAddress)) { uc.ReanalysisRequired = ReanalysisScope.DataOnly; } else if (oldFormat == null || newFormat == null || oldFormat.Length != newFormat.Length) { uc.ReanalysisRequired = ReanalysisScope.DataOnly; } else { uc.ReanalysisRequired = ReanalysisScope.None; } return(uc); }
/// <summary> /// Creates an UndoableChange for an address map update. /// </summary> /// <param name="offset">Affected offset.</param> /// <param name="oldAddress">Previous address map entry, or -1 if none.</param> /// <param name="newAddress">New address map entry, or -1 if none.</param> /// <returns>Change record.</returns> public static UndoableChange CreateAddressChange(int offset, int oldAddress, int newAddress) { if (oldAddress == newAddress) { Debug.WriteLine("No-op address change at +" + offset.ToString("x6") + ": " + oldAddress); } UndoableChange uc = new UndoableChange(); uc.Type = ChangeType.SetAddress; uc.Offset = offset; uc.OldValue = oldAddress; uc.NewValue = newAddress; uc.ReanalysisRequired = ReanalysisScope.CodeAndData; return(uc); }
/// <summary> /// Creates an UndoableChange for a note update. /// </summary> /// <param name="offset">Affected offset.</param> /// <param name="oldNote">Current note.</param> /// <param name="newNote">New note.</param> /// <returns>Change record.</returns> public static UndoableChange CreateNoteChange(int offset, MultiLineComment oldNote, MultiLineComment newNote) { if (oldNote == newNote) { Debug.WriteLine("No-op note change at +" + offset.ToString("x6") + ": " + oldNote); } UndoableChange uc = new UndoableChange(); uc.Type = ChangeType.SetNote; uc.Offset = offset; uc.OldValue = oldNote; uc.NewValue = newNote; uc.ReanalysisRequired = ReanalysisScope.None; return(uc); }
/// <summary> /// Creates an UndoableChange for a long comment update. /// </summary> /// <param name="offset">Affected offset.</param> /// <param name="oldComment">Current comment.</param> /// <param name="newComment">New comment.</param> /// <returns>Change record.</returns> public static UndoableChange CreateLongCommentChange(int offset, MultiLineComment oldComment, MultiLineComment newComment) { if (oldComment == newComment) { Debug.WriteLine("No-op long comment change at +" + offset.ToString("x6") + ": " + oldComment); } UndoableChange uc = new UndoableChange(); uc.Type = ChangeType.SetLongComment; uc.Offset = offset; uc.OldValue = oldComment; uc.NewValue = newComment; uc.ReanalysisRequired = ReanalysisScope.None; return(uc); }
/// <summary> /// Creates an UndoableChange for a status flag override update. /// </summary> /// <param name="offset">Affected offset.</param> /// <param name="oldFlags">Current flags.</param> /// <param name="newFlags">New flags.</param> /// <returns></returns> public static UndoableChange CreateStatusFlagChange(int offset, StatusFlags oldFlags, StatusFlags newFlags) { if (oldFlags == newFlags) { Debug.WriteLine("No-op status flag change at " + offset); } UndoableChange uc = new UndoableChange(); uc.Type = ChangeType.SetStatusFlagOverride; uc.Offset = offset; uc.OldValue = oldFlags; uc.NewValue = newFlags; // This can affect instruction widths (for M/X) and conditional branches. We // don't need to re-analyze for changes to I/D, but users don't really need to // change those anyway, so it's not worth optimizing. uc.ReanalysisRequired = ReanalysisScope.CodeAndData; return(uc); }
/// <summary> /// Creates an UndoableChange for a type hint update. Rather than adding a /// separate UndoableChange for each affected offset -- which could span the /// entire file -- we use range sets to record the before/after state. /// </summary> /// <param name="undoSet">Current values.</param> /// <param name="newSet">New values.</param> /// <returns>Change record.</returns> public static UndoableChange CreateTypeHintChange(TypedRangeSet undoSet, TypedRangeSet newSet) { if (newSet.Count == 0) { Debug.WriteLine("Empty hint change?"); } UndoableChange uc = new UndoableChange(); uc.Type = ChangeType.SetTypeHint; uc.Offset = -1; uc.OldValue = undoSet; uc.NewValue = newSet; // Any hint change can affect whether something is treated as code. // Either we're deliberately setting it as code or non-code, or we're // setting it to "no hint", which means the code analyzer gets // to make the decision now. This requires a full code+data re-analysis. uc.ReanalysisRequired = ReanalysisScope.CodeAndData; return(uc); }
/// <summary> /// Creates an UndoableChange for a change to the project properties. /// </summary> /// <param name="oldNote">Current note.</param> /// <param name="newNote">New note.</param> /// <returns>Change record.</returns> public static UndoableChange CreateProjectPropertiesChange(ProjectProperties oldProps, ProjectProperties newProps) { Debug.Assert(oldProps != null && newProps != null); if (oldProps == newProps) // doesn't currently work except as reference check { Debug.WriteLine("No-op property change: " + oldProps); } UndoableChange uc = new UndoableChange(); uc.Type = ChangeType.SetProjectProperties; uc.Offset = -1; uc.OldValue = oldProps; uc.NewValue = newProps; // Project properties could change the CPU type, requiring a full code+data // reanalysis. We could scan the objects to see what actually changed, but that // doesn't seem worthwhile. uc.ReanalysisRequired = ReanalysisScope.CodeAndData; return(uc); }