1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-11-29 10:50:28 +00:00

Rework NavStack

Instead of traversing a single dual-element stack, use separate
stacks for forward and backward.

Record whether the jump was from a Note, so we select the right
set of lines when we return to it.

If nothing is selected, push the current top position on, instead
of doing nothing at all.

Correctly handle the case where somebody is trying to jump to the
current position.
This commit is contained in:
Andy McFadden 2019-06-22 11:27:21 -07:00
parent c87d79ec9e
commit 0041584d2e
3 changed files with 122 additions and 98 deletions

View File

@ -123,7 +123,7 @@ namespace Asm65 {
char mSregChar; char mSregChar;
// Format string for offsets. // Format string for offsets.
private string mOffset20Format; private string mOffset24Format;
// Format strings for addresses. // Format strings for addresses.
private string mAddrFormatNoBank; private string mAddrFormatNoBank;
@ -231,7 +231,7 @@ namespace Asm65 {
private void Reset() { private void Reset() {
// Clear old data. (No longer needed.) // Clear old data. (No longer needed.)
//mAddrFormatNoBank = mAddrFormatWithBank = null; //mAddrFormatNoBank = mAddrFormatWithBank = null;
//mOffset20Format = null; //mOffset24Format = null;
//mOpcodeStrings.Clear(); //mOpcodeStrings.Clear();
//mPseudoOpStrings.Clear(); //mPseudoOpStrings.Clear();
//mOperandFormats.Clear(); //mOperandFormats.Clear();
@ -274,10 +274,10 @@ namespace Asm65 {
/// <param name="offset">Offset to format.</param> /// <param name="offset">Offset to format.</param>
/// <returns>Formatted string.</returns> /// <returns>Formatted string.</returns>
public string FormatOffset24(int offset) { public string FormatOffset24(int offset) {
if (string.IsNullOrEmpty(mOffset20Format)) { if (string.IsNullOrEmpty(mOffset24Format)) {
mOffset20Format = "+{0:" + mHexFmtChar + "6}"; mOffset24Format = "+{0:" + mHexFmtChar + "6}";
} }
return string.Format(mOffset20Format, offset & 0x0fffff); return string.Format(mOffset24Format, offset & 0x0fffff);
} }
/// <summary> /// <summary>

View File

@ -1299,7 +1299,12 @@ namespace SourceGenWPF {
/// <param name="gotoOffset">Offset to jump to.</param> /// <param name="gotoOffset">Offset to jump to.</param>
/// <param name="doPush">If set, push new offset onto navigation stack.</param> /// <param name="doPush">If set, push new offset onto navigation stack.</param>
public void GoToOffset(int gotoOffset, bool jumpToNote, bool doPush) { public void GoToOffset(int gotoOffset, bool jumpToNote, bool doPush) {
int curSelIndex = mMainWin.CodeListView_GetFirstSelectedIndex(); NavStack.Location prevLoc = GetCurrentlySelectedLocation();
if (gotoOffset == prevLoc.Offset && jumpToNote == prevLoc.IsNote) {
// we're jumping to ourselves?
Debug.WriteLine("Ignoring goto to current position");
return;
}
int topLineIndex = CodeLineList.FindLineIndexByOffset(gotoOffset); int topLineIndex = CodeLineList.FindLineIndexByOffset(gotoOffset);
if (topLineIndex < 0) { if (topLineIndex < 0) {
@ -1339,26 +1344,29 @@ namespace SourceGenWPF {
mMainWin.CodeListView_SelectRange(topLineIndex, lastLineIndex - topLineIndex); mMainWin.CodeListView_SelectRange(topLineIndex, lastLineIndex - topLineIndex);
if (doPush) { if (doPush) {
if (curSelIndex >= 0) { // Update the back stack and associated controls.
// Update the back stack and associated controls. mNavStack.Push(prevLoc);
mNavStack.Push(CodeLineList[curSelIndex].FileOffset, gotoOffset);
#if false
UpdateMenuItemsAndTitle();
#endif
} else {
// This can happen when the project is first opened and nothing is selected.
Debug.WriteLine("no selection to go back to");
}
} }
} }
private NavStack.Location GetCurrentlySelectedLocation() {
int index = mMainWin.CodeListView_GetFirstSelectedIndex();
if (index < 0) {
// nothing selected, use top instead
index = mMainWin.CodeListView_GetTopIndex();
}
int offset = CodeLineList[index].FileOffset;
bool isNote = (CodeLineList[index].LineType == LineListGen.Line.Type.Note);
return new NavStack.Location(offset, isNote);
}
public bool CanNavigateBackward() { public bool CanNavigateBackward() {
return mNavStack.HasBackward; return mNavStack.HasBackward;
} }
public void NavigateBackward() { public void NavigateBackward() {
Debug.Assert(mNavStack.HasBackward); Debug.Assert(mNavStack.HasBackward);
int backOff = mNavStack.Pop(); NavStack.Location backLoc = mNavStack.MoveBackward(GetCurrentlySelectedLocation());
GoToOffset(backOff, false, false); GoToOffset(backLoc.Offset, backLoc.IsNote, false);
} }
public bool CanNavigateForward() { public bool CanNavigateForward() {
@ -1366,8 +1374,8 @@ namespace SourceGenWPF {
} }
public void NavigateForward() { public void NavigateForward() {
Debug.Assert(mNavStack.HasForward); Debug.Assert(mNavStack.HasForward);
int fwdOff = mNavStack.PushPrevious(); NavStack.Location fwdLoc = mNavStack.MoveForward(GetCurrentlySelectedLocation());
GoToOffset(fwdOff, false, false); GoToOffset(fwdLoc.Offset, fwdLoc.IsNote, false);
} }
/// <summary> /// <summary>

View File

@ -23,43 +23,60 @@ namespace SourceGenWPF {
/// Maintains a record of interesting places we've been. /// Maintains a record of interesting places we've been.
/// </summary> /// </summary>
public class NavStack { public class NavStack {
// If you're at offset 10, and you jump to offset 20, we push offset 10 onto the // It's tempting to use a single stack, and just move a cursor up and down. However,
// back list. If you hit back, you want to be at offset 10. If you then hit // that doesn't quite work. We always want to record where you came from, so we're
// forward, you want to jump to offset 20. So how does 20 get on there? // pushing locations on when moving both forward and backward.
// //
// The trick is to record the "from" and "to" position at each step. When moving // If you move backward and then jump somewhere else, we want to discard the list of
// backward we go the previous "from" position. When moving forward we move to // previously-recorded forward places.
// the next "to" position. This makes the movement asymmetric, but it means that //
// that forward movement is always to places we've jumped to, and backward movement // Jumping to Notes is a little different from jumping to anything else, because we
// is to places we jumped away from. // want to highlight the note rather than the code at the associated offset. This
// is especially important when moving upward through the file, or the note will be
// off the top of the screen.
// TODO(someday): this can be simplified(?) to use a pair of stacks, one for moving // TODO(someday): change the back button to a pop-up list of locations (like the way
// forward, one for moving backward. Traversing the stack requires popping off one // VS 2017 does it).
// and pushing onto the other, rather than moving the cursor. No change in
// behavior, but potentially easier to make sense of.
// TODO(someday): record more about what was selected, so e.g. when we move back or
// forward to a Note we can highlight it appropriately.
// TODO(someday): once we have the above, we can change the back button to a pop-up
// list of locations (like the way VS 2017 does it).
private class OffsetPair { /// <summary>
public int From { get; set; } /// Holds enough information to get us back where we were, in style.
public int To { get; set; } /// </summary>
public class Location {
public int Offset { get; set; }
public bool IsNote { get; set; }
public OffsetPair(int from, int to) { public Location(int offset, bool isNote) {
From = from; Offset = offset;
To = to; IsNote = isNote;
} }
public override string ToString() { public override string ToString() {
return "[fr=+" + From.ToString("x6") + " to=+" + To.ToString("x6") + "]"; return string.Format("[+{0:x6},{1}]", Offset, IsNote);
}
public static bool operator ==(Location a, Location b) {
if (ReferenceEquals(a, b)) {
return true; // same object, or both null
}
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) {
return false; // one is null
}
return a.Offset == b.Offset && a.IsNote == b.IsNote;
}
public static bool operator !=(Location a, Location b) {
return !(a == b);
}
public override bool Equals(object obj) {
return obj is Location && this == (Location)obj;
}
public override int GetHashCode() {
return Offset + (IsNote ? 65536 : 0);
} }
} }
// Offset stack. Popped items remain in place temporarily. // Location stacks.
private List<OffsetPair> mStack = new List<OffsetPair>(); private List<Location> mBackStack = new List<Location>();
private List<Location> mFwdStack = new List<Location>();
// Current stack position. This is one past the most-recently-pushed element.
private int mCursor = 0;
public NavStack() { } public NavStack() { }
@ -69,7 +86,7 @@ namespace SourceGenWPF {
/// </summary> /// </summary>
public bool HasBackward { public bool HasBackward {
get { get {
return mCursor > 0; return mBackStack.Count > 0;
} }
} }
@ -78,78 +95,77 @@ namespace SourceGenWPF {
/// </summary> /// </summary>
public bool HasForward { public bool HasForward {
get { get {
return mCursor < mStack.Count; return mFwdStack.Count > 0;
} }
} }
/// <summary> /// <summary>
/// Clears the back stack. /// Clears the stacks.
/// </summary> /// </summary>
public void Clear() { public void Clear() {
mStack.Clear(); mBackStack.Clear();
mCursor = 0; mFwdStack.Clear();
} }
/// <summary> /// <summary>
/// Pops the top entry off the stack. This moves the cursor but doesn't actually /// Pushes a new entry onto the back stack. Clears the forward stack.
/// remove the item.
/// </summary>
/// <returns>The "from" element of the popped entry.</returns>
public int Pop() {
if (mCursor == 0) {
throw new Exception("Stack is empty");
}
mCursor--;
//Debug.WriteLine("NavStack popped +" + mStack[mCursor] +
// " (now cursor=" + mCursor + ") -- " + this);
return mStack[mCursor].From;
}
/// <summary>
/// Pushes a new entry onto the stack at the cursor. If there were additional
/// entries past the cursor, they will be discarded.
/// ///
/// If the same entry is already at the top of the stack, the entry will not be added. /// If the same entry is already at the top of the stack, the entry will not be added.
/// </summary> /// </summary>
/// <param name="fromOffset">File offset associated with line we are moving from. /// <param name="curLoc">Current location.</param>
/// This may be negative if we're moving from a header comment or .EQ directive.</param> public void Push(Location curLoc) {
/// <param name="toOffset">File offset associated with line we are moving to. This if (mBackStack.Count > 0 && mBackStack[mBackStack.Count - 1] == curLoc) {
/// may be negative if we're moving to the header comment or a .EQ directive.</param> Debug.WriteLine("Not re-pushing " + curLoc);
public void Push(int fromOffset, int toOffset) { return;
if (mStack.Count > mCursor) {
mStack.RemoveRange(mCursor, mStack.Count - mCursor);
} }
OffsetPair newPair = new OffsetPair(fromOffset, toOffset);
mStack.Add(newPair); mBackStack.Add(curLoc);
mCursor++; mFwdStack.Clear();
//Debug.WriteLine("NavStack pushed +" + newPair + " -- " + this);
//Debug.WriteLine("Stack now: " + this);
} }
/// <summary> /// <summary>
/// Pushes a previous entry back onto the stack. /// Pops the top element from the back stack, and pushes the current position
/// onto the forward stack.
/// </summary> /// </summary>
/// <returns>The "to" element of the pushed entry.</returns> /// <param name="fromLoc">Current location.</param>
public int PushPrevious() { /// <returns>The location to move to.</returns>
if (mCursor == mStack.Count) { public Location MoveBackward(Location fromLoc) {
throw new Exception("At top of stack"); if (mBackStack.Count == 0) {
throw new Exception("Stack is empty");
} }
int fwdOff = mStack[mCursor].To; Location toLoc = mBackStack[mBackStack.Count - 1];
mCursor++; mBackStack.RemoveAt(mBackStack.Count - 1);
//Debug.WriteLine("NavStack pushed prev (now cursor=" + mCursor + ") -- " + this); mFwdStack.Add(fromLoc);
return fwdOff; return toLoc;
}
/// <summary>
/// Pops the top element from the forward stack, and pushes the current position
/// onto the back stack.
/// </summary>
/// <param name="fromLoc">Current location.</param>
/// <returns>The location to move to.</returns>
public Location MoveForward(Location fromLoc) {
if (mFwdStack.Count == 0) {
throw new Exception("Stack is empty");
}
Location toLoc = mFwdStack[mFwdStack.Count - 1];
mFwdStack.RemoveAt(mFwdStack.Count - 1);
mBackStack.Add(fromLoc);
return toLoc;
} }
public override string ToString() { public override string ToString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.Append("NavStack:"); sb.Append("Back:");
for (int i = 0; i < mStack.Count; i++) { foreach (Location loc in mBackStack) {
if (i == mCursor) { sb.Append(loc);
sb.Append(" [*]");
}
sb.Append(mStack[i]);
} }
if (mCursor == mStack.Count) { sb.Append(" Fwd:");
sb.Append(" [*]"); foreach (Location loc in mFwdStack) {
sb.Append(loc);
} }
return sb.ToString(); return sb.ToString();
} }