Skip to content

Commit c2fa0bc

Browse files
authored
Merge pull request #137 from Martian-Technologies/undo-tree
Undo tree system (Patrick)
2 parents 9aa5e5a + e25061e commit c2fa0bc

File tree

5 files changed

+303
-7
lines changed

5 files changed

+303
-7
lines changed

src/backend/circuit/undoSystem.h

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,45 @@
11
#ifndef undoSystem_h
22
#define undoSystem_h
33

4+
#include "undoTree/undoTree.h"
45
#include "backend/container/difference.h"
56

67
class UndoSystem {
78
public:
8-
inline UndoSystem() : undoPosition(0) { }
9+
inline UndoSystem(): tree(), undoPosition(tree.begin()) { }
910

10-
inline void addDifference(DifferenceSharedPtr difference) { while (undoPosition < differences.size()) differences.pop_back(); ++undoPosition; differences.push_back(difference); }
11-
inline DifferenceSharedPtr undoDifference() { if (undoPosition == 0) return std::make_shared<Difference>(); return differences[--undoPosition]; }
12-
inline DifferenceSharedPtr redoDifference() { while (undoPosition == differences.size()) return std::make_shared<Difference>(); return differences[undoPosition++]; }
11+
inline void addDifference(DifferenceSharedPtr difference) {
12+
undoPosition = tree.insert(undoPosition, difference);
13+
}
14+
inline DifferenceSharedPtr undoDifference() {
15+
DifferenceSharedPtr& temp = *undoPosition;
16+
if (undoPosition != tree.begin()) {
17+
if (!onSameBranch(undoPosition, undoPosition.prev())) {
18+
redoPath.push(std::make_pair(undoPosition.prev(), undoPosition.whichBranch()));
19+
}
20+
undoPosition = undoPosition.prev();
21+
}
22+
return temp;
23+
}
24+
inline DifferenceSharedPtr redoDifference() {
25+
int goDown = -1;
26+
if (!redoPath.empty()) {
27+
if (undoPosition == redoPath.top().first) {
28+
goDown = redoPath.top().second;
29+
redoPath.pop();
30+
}
31+
}
1332

14-
private:
15-
unsigned int undoPosition;
16-
std::vector<DifferenceSharedPtr> differences;
33+
if (undoPosition.next(goDown) != tree.end()) {
34+
undoPosition = undoPosition.next(goDown);
35+
return *undoPosition;
36+
} else return DifferenceSharedPtr(new Difference);
37+
}
1738

39+
private:
40+
UndoTree tree;
41+
UndoTree::iterator undoPosition;
42+
std::stack<std::pair<UndoTree::iterator, int>> redoPath;
1843
};
1944

2045
#endif /* undoSystem_h */
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#include "undoTree.h"
2+
3+
// ================================================================================================
4+
// Class UndoTree implementation
5+
6+
UndoTree::UndoTree():
7+
mainBranch(new Branch(this)), numNodes(0) {
8+
branches.insert(mainBranch);
9+
mainBranch->nodes.emplace_back(DifferenceSharedPtr(new Difference));
10+
}
11+
UndoTree::~UndoTree() {
12+
for (Branch* branch : branches) {
13+
delete branch;
14+
}
15+
branches.clear();
16+
mainBranch = nullptr;
17+
}
18+
19+
size_t UndoTree::size() const {
20+
return numNodes;
21+
}
22+
size_t UndoTree::numBranches() const {
23+
return branches.size();
24+
}
25+
26+
UndoTree::iterator UndoTree::insert(const iterator& it, DifferenceSharedPtr diff) {
27+
// Make sure the iterator is on this tree
28+
assert(branches.contains(it.branch));
29+
30+
// If the iterator is at the end of its branch append the new diff
31+
if (it.pos == it.branch->nodes.size() - 1) {
32+
it.branch->nodes.emplace_back(Branch::Node(diff));
33+
return it.next();
34+
// If the iterator is not at the end of its branch make a new branch
35+
} else {
36+
Branch* newBranch = new Branch(this, it.branch, it.pos, diff);
37+
branches.insert(newBranch);
38+
if (it.branch->nodes[it.pos].branches == nullptr) {
39+
it.branch->nodes[it.pos].branches = new std::vector<Branch*>;
40+
}
41+
it.branch->nodes[it.pos].branches->push_back(newBranch);
42+
return iterator(newBranch, 0);
43+
}
44+
numNodes++;
45+
}
46+
47+
void UndoTree::clear() {
48+
for (Branch* branch : branches) {
49+
delete branch;
50+
}
51+
branches.clear();
52+
mainBranch = new Branch(this);
53+
mainBranch->nodes.emplace_back(DifferenceSharedPtr(new Difference));
54+
branches.insert(mainBranch);
55+
numNodes = 0;
56+
}
57+
58+
void UndoTree::prune(const iterator& begin) {
59+
// Make sure the iterator is on this tree
60+
assert(begin.branch->tree == this);
61+
// Delete all branches that originate after begin
62+
for (int i = begin.pos; i < begin.branch->nodes.size(); i++) {
63+
if (begin.branch->nodes[begin.pos].branches != nullptr) {
64+
for (Branch* b : *begin.branch->nodes[begin.pos].branches) {
65+
branches.erase(b);
66+
numNodes -= b->nodes.size();
67+
delete b;
68+
}
69+
}
70+
}
71+
// Remove all diffs after begin
72+
numNodes -= begin.branch->nodes.size() - begin.pos;
73+
begin.branch->nodes.erase(begin.branch->nodes.begin() + begin.pos);
74+
}
75+
76+
UndoTree::iterator UndoTree::begin() {
77+
return iterator(mainBranch, 0);
78+
}
79+
80+
UndoTree::iterator UndoTree::end() {
81+
return iterator(mainBranch, -1);
82+
}
83+
84+
// ================================================================================================
85+
// Class UndoTree::iterator implementation
86+
87+
UndoTree::iterator::iterator(Branch* branch, int pos):
88+
branch(branch), pos(pos) { }
89+
90+
UndoTree::iterator UndoTree::iterator::next(int whichBranch) const {
91+
if (whichBranch == -1) {
92+
if (pos + 1 == this->branch->nodes.size()) {
93+
return iterator(this->branch, -1);
94+
} else return iterator(this->branch, pos + 1);
95+
} else return iterator((*this->branch->nodes[pos].branches)[whichBranch], 0);
96+
}
97+
98+
UndoTree::iterator UndoTree::iterator::prev() const {
99+
if (pos == -1) {
100+
return iterator(this->branch, this->branch->nodes.size() - 1);
101+
} else if (pos == 0) {
102+
103+
return iterator(this->branch->parentBranch, this->branch->parentNode);
104+
} else return iterator(this->branch, pos - 1);
105+
}
106+
107+
int UndoTree::iterator::numBranches() const {
108+
if (branch->nodes[pos].branches == nullptr) {
109+
return 0;
110+
} else return branch->nodes[pos].branches->size();
111+
}
112+
113+
int UndoTree::iterator::whichBranch() const {
114+
if (branch->parentBranch == nullptr) {
115+
return -1;
116+
} else {
117+
UndoTree::Branch::Node& parentNode = branch->parentBranch->nodes[branch->parentNode];
118+
for (size_t i = 0; i < parentNode.branches->size(); i++) {
119+
if (branch == (*parentNode.branches)[i]) {
120+
return i;
121+
}
122+
}
123+
// Control should never reach here
124+
assert(false);
125+
return INT_MAX;
126+
}
127+
}
128+
129+
DifferenceSharedPtr& UndoTree::iterator::operator*() {
130+
return branch->nodes[pos].diff;
131+
}
132+
133+
bool UndoTree::iterator::operator==(const iterator& other) const {
134+
// -1 denotes end iterator
135+
if (this->pos == other.pos && other.pos == -1) {
136+
return true;
137+
// Otherwise check if they are at same place on same branch
138+
} else return this->pos == other.pos && this->branch == other.branch;
139+
}
140+
141+
bool UndoTree::iterator::operator!=(const iterator& other) const {
142+
return !(*this == other);
143+
}
144+
145+
// ================================================================================================
146+
// Class UndoTree::Branch implementation
147+
148+
UndoTree::Branch::Branch(UndoTree* tree):
149+
tree(tree), parentBranch(nullptr), parentNode(-1) { }
150+
UndoTree::Branch::Branch(UndoTree* tree, Branch* parentBranch, int parentNode, DifferenceSharedPtr diff):
151+
tree(tree), parentBranch(parentBranch), parentNode(parentNode) {
152+
nodes.emplace_back(Node(diff));
153+
}
154+
155+
// ================================================================================================
156+
// Class UndoTree::Branch::Node implementation
157+
158+
UndoTree::Branch::Node::Node(DifferenceSharedPtr diff):
159+
diff(diff), branches(nullptr) { }
160+
161+
UndoTree::Branch::Node::~Node() {
162+
delete branches;
163+
}
164+
165+
bool onSameBranch(const UndoTree::iterator& a, const UndoTree::iterator& b) {
166+
return a.branch == b.branch;
167+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#ifndef undoTree_h
2+
#define undoTree_h
3+
4+
#include "../../container/difference.h"
5+
6+
class UndoTree {
7+
public:
8+
class iterator;
9+
UndoTree();
10+
~UndoTree();
11+
12+
size_t size() const;
13+
size_t numBranches() const;
14+
15+
/**
16+
* @brief Inserts a diff to the UndoTree.
17+
*
18+
* Appends the diff to an existing branch if the iterator is at
19+
* the end of the branch. Otherwise, a new branch is created with
20+
* the new diff as the first element.
21+
*
22+
* @param it An iterator to the element the new diff will be inserted after.
23+
* @param diff A DifferenceSharedPtr to the new diff.
24+
* @returns An iterator to the inserted diff.
25+
*/
26+
iterator insert(const iterator& it, DifferenceSharedPtr diff);
27+
28+
/**
29+
* @brief Clears the contents of the UndoTree.
30+
*/
31+
void clear();
32+
33+
/**
34+
* @brief Removes all elements and branches after a given element (inclusive).
35+
* @param begin An iterator to the first element to remove.
36+
*/
37+
void prune(const iterator& begin);
38+
39+
iterator begin();
40+
iterator end();
41+
private:
42+
class Branch;
43+
Branch* mainBranch;
44+
std::unordered_set<Branch*> branches;
45+
size_t numNodes;
46+
};
47+
48+
struct UndoTree::Branch {
49+
struct Node {
50+
Node(DifferenceSharedPtr diff);
51+
~Node();
52+
DifferenceSharedPtr diff;
53+
std::vector<Branch*>* branches;
54+
};
55+
56+
Branch(UndoTree* tree);
57+
Branch(UndoTree* tree, Branch* parentBranch, int parentNode, DifferenceSharedPtr diff);
58+
59+
UndoTree* tree;
60+
Branch* parentBranch;
61+
int parentNode;
62+
std::vector<Node> nodes;
63+
};
64+
65+
class UndoTree::iterator {
66+
friend class UndoTree;
67+
friend bool onSameBranch(const UndoTree::iterator& a, const UndoTree::iterator& b);
68+
public:
69+
/**
70+
* @brief Goes to the next diff along the specified branch.
71+
* @param whichBranch The branch to move into.
72+
* @returns An iterator to the next diff.
73+
*/
74+
iterator next(int whichBranch = -1) const;
75+
76+
/**
77+
* @brief Goes to the previous diff.
78+
* @returns An iterator to the previous diff.
79+
*/
80+
iterator prev() const;
81+
82+
/**
83+
* @brief Finds the number of branches that split off from the current diff.
84+
*/
85+
int numBranches() const;
86+
87+
/**
88+
* @brief Returns which child of the parent node this branch is, or -1 if this branch is main.
89+
*/
90+
int whichBranch() const;
91+
92+
DifferenceSharedPtr& operator*();
93+
bool operator==(const iterator& other) const;
94+
bool operator!=(const iterator& other) const;
95+
private:
96+
iterator(Branch* branch, int pos);
97+
Branch* branch;
98+
int pos;
99+
};
100+
101+
#endif

src/backend/container/difference.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define difference_h
33

44
#include "block/blockDefs.h"
5+
#include "../position/position.h"
56

67
class Difference {
78
friend class BlockContainer;

src/precompiled.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
#include <optional>
88
#include <utility>
99
#include <memory>
10+
#include <cassert>
1011

1112
#include <unordered_map>
1213
#include <unordered_set>
1314
#include <string>
1415
#include <variant>
1516
#include <vector>
1617
#include <array>
18+
#include <stack>
1719
#include <queue>
1820
#include <map>
1921
#include <set>

0 commit comments

Comments
 (0)