/*
* Copyright 2019 faddenSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace SourceGen {
///
/// Maintains a record of interesting places we've been.
///
public class NavStack {
// It's tempting to use a single stack, and just move a cursor up and down. However,
// that doesn't quite work. We always want to record where you came from, so we're
// pushing locations on when moving both forward and backward.
//
// If you move backward and then jump somewhere else, we want to discard the list of
// previously-recorded forward places.
//
// Jumping to Notes is a little different from jumping to anything else, because we
// want to select the entire 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): change the back button to a pop-up list of locations (like the way
// VS 2017 does it).
public enum GoToMode {
Unknown = 0,
JumpToCodeData, // destination is first byte of code or data at target offset
JumpToNote, // destination is Note at target offset
JumpToAdjIndex, // destination is first line at target offset plus LineDelta
JumpToArStart, // destination is arstart at target offset.
JumpToArEnd, // destination is arend at target offset
};
///
/// Holds enough information to get us back where we were, in style.
///
public class Location {
///
/// Code/data offset. May be less than zero for equates.
///
public int Offset { get; set; }
///
/// Number of lines between the first line at the specified offset, and the
/// line we actually want to land on. Used when Mode=JumpToAdjIndex.
///
///
/// It's possible this line no longer exists. Easiest test is to compare the
/// offset of the unadjusted line to the offset of the adjusted line. If not
/// equal, ignore the delta.
///
public int LineDelta { get; set; }
///
/// Specifies interesting things to find at the target offset.
///
public GoToMode Mode { get; set; }
public Location(int offset, int lineDelta, GoToMode mode) {
Offset = offset;
LineDelta = lineDelta;
Mode = mode;
//if (lineDelta != 0 && mode != GoToMode.JumpToAdjIndex) {
// Debug.WriteLine("HEY: lineDelta=" + lineDelta + " mode=" + mode);
//}
}
public override string ToString() {
return string.Format("[+{0:x6},{1},{2}]", Offset, LineDelta, Mode);
}
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.LineDelta == b.LineDelta && a.Mode == b.Mode;
}
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 + (LineDelta * 1000000) + ((int)Mode << 24);
}
}
// Location stacks.
private List mBackStack = new List();
private List mFwdStack = new List();
public NavStack() { }
///
/// True if there is an opportunity to pop backward.
///
public bool HasBackward {
get {
return mBackStack.Count > 0;
}
}
///
/// True if there is an opportunity to push forward.
///
public bool HasForward {
get {
return mFwdStack.Count > 0;
}
}
///
/// Clears the stacks.
///
public void Clear() {
mBackStack.Clear();
mFwdStack.Clear();
}
///
/// Pushes a new entry onto the back stack. Clears the forward stack.
///
/// If the same entry is already at the top of the stack, the entry will not be added.
///
/// Current location.
public void Push(Location curLoc) {
if (mBackStack.Count > 0 && mBackStack[mBackStack.Count - 1] == curLoc) {
Debug.WriteLine("Not re-pushing " + curLoc);
return;
}
mBackStack.Add(curLoc);
mFwdStack.Clear();
//Debug.WriteLine("Stack now: " + this);
}
///
/// Pops the top element from the back stack, and pushes the current position
/// onto the forward stack.
///
/// Current location.
/// The location to move to.
public Location MoveBackward(Location fromLoc) {
if (mBackStack.Count == 0) {
throw new Exception("Stack is empty");
}
Location toLoc = mBackStack[mBackStack.Count - 1];
mBackStack.RemoveAt(mBackStack.Count - 1);
mFwdStack.Add(fromLoc);
return toLoc;
}
///
/// Pops the top element from the forward stack, and pushes the current position
/// onto the back stack.
///
/// Current location.
/// The location to move to.
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() {
StringBuilder sb = new StringBuilder();
sb.Append("Back:");
foreach (Location loc in mBackStack) {
sb.Append(loc);
}
sb.Append(" Fwd:");
foreach (Location loc in mFwdStack) {
sb.Append(loc);
}
return sb.ToString();
}
}
}