aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/UndoHistory.h
blob: 5ddc035938ab69e7540debe0e69ce3297635bfd8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Scintilla source code edit control
/** @file UndoHistory.h
 ** Manages undo for the document.
 **/
// Copyright 1998-2024 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#ifndef UNDOHISTORY_H
#define UNDOHISTORY_H

namespace Scintilla::Internal {

// ScaledVector is a vector of unsigned integers that uses elements sized to hold the largest value.
// Thus, if an undo history only contains short insertions and deletions the lengths vector may
// only use 2 bytes or even 1 byte for each length.
// This saves much memory often reducing by 50% for 32-bit builds and 75% for 64-bit builds.

class ScaledVector {
	size_t elementSize = 1;
	size_t elementMax = 255;
	std::vector<uint8_t> bytes;
public:
	[[nodiscard]] size_t Size() const noexcept;
	[[nodiscard]] size_t ValueAt(size_t index) const noexcept;
	[[nodiscard]] intptr_t SignedValueAt(size_t index) const noexcept;
	void SetValueAt(size_t index, size_t value);
	void ClearValueAt(size_t index) noexcept;
	void Clear() noexcept;
	void Truncate(size_t length) noexcept;
	void ReSize(size_t length);
	void PushBack();

	// For testing
	[[nodiscard]] size_t SizeInBytes() const noexcept;
};

class UndoActionType {
public:
	ActionType at : 4;
	bool mayCoalesce : 1;
	UndoActionType() noexcept;
};

struct UndoActions {
	std::vector<UndoActionType> types;
	ScaledVector positions;
	ScaledVector lengths;

	UndoActions() noexcept;
	void Truncate(size_t length) noexcept;
	void PushBack();
	void Clear() noexcept;
	[[nodiscard]] intptr_t SSize() const noexcept;
	void Create(size_t index, ActionType at_, Sci::Position position_, Sci::Position lenData_, bool mayCoalesce_);
	[[nodiscard]] bool AtStart(size_t index) const noexcept;
	[[nodiscard]] size_t LengthTo(size_t index) const noexcept;
};

class ScrapStack {
	std::string stack;
	size_t current = 0;
public:
	void Clear() noexcept;
	const char *Push(const char *text, size_t length);
	void SetCurrent(size_t position) noexcept;
	void MoveForward(size_t length) noexcept;
	void MoveBack(size_t length) noexcept;
	[[nodiscard]] const char *CurrentText() const noexcept;
	[[nodiscard]] const char *TextAt(size_t position) const noexcept;
};

constexpr int coalesceFlag = 0x100;

/**
 *
 */
class UndoHistory {
	UndoActions actions;
	int currentAction = 0;
	int undoSequenceDepth = 0;
	int savePoint = 0;
	int tentativePoint = -1;
	std::optional<int> detach;
	std::unique_ptr<ScrapStack> scraps;
	struct actPos { int act; size_t position; };
	std::optional<actPos> memory;

	int PreviousAction() const noexcept;

public:
	UndoHistory();
	~UndoHistory() noexcept;

	const char *AppendAction(ActionType at, Sci::Position position, const char *data, Sci::Position lengthData, bool &startSequence, bool mayCoalesce=true);

	void BeginUndoAction() noexcept;
	void EndUndoAction() noexcept;
	void DropUndoSequence() noexcept;
	void DeleteUndoHistory() noexcept;

	[[nodiscard]] int Actions() const noexcept;

	/// The save point is a marker in the undo stack where the container has stated that
	/// the buffer was saved. Undo and redo can move over the save point.
	void SetSavePoint(int action) noexcept;
	[[nodiscard]] int SavePoint() const noexcept;
	void SetSavePoint() noexcept;
	bool IsSavePoint() const noexcept;
	bool BeforeSavePoint() const noexcept;
	bool BeforeOrAtSavePoint() const noexcept;
	bool BeforeReachableSavePoint() const noexcept;
	bool AfterSavePoint() const noexcept;

	/// The detach point is the last action that was before an inaccessible missing save point.
	void SetDetachPoint(int action) noexcept;
	[[nodiscard]] int DetachPoint() const noexcept;
	bool AfterDetachPoint() const noexcept;
	bool AfterOrAtDetachPoint() const noexcept;

	[[nodiscard]] intptr_t Delta(int action) noexcept;
	[[nodiscard]] bool Validate(intptr_t lengthDocument) noexcept;
	void SetCurrent(int action, intptr_t lengthDocument);
	[[nodiscard]] int Current() const noexcept;
	[[nodiscard]] int Type(int action) const noexcept;
	[[nodiscard]] Sci::Position Position(int action) const noexcept;
	[[nodiscard]] Sci::Position Length(int action) const noexcept;
	[[nodiscard]] std::string_view Text(int action) noexcept;
	void PushUndoActionType(int type, Sci::Position position);
	void ChangeLastUndoActionText(size_t length, const char *text);

	// Tentative actions are used for input composition so that it can be undone cleanly
	void SetTentative(int action) noexcept;
	[[nodiscard]] int TentativePoint() const noexcept;
	void TentativeStart() noexcept;
	void TentativeCommit() noexcept;
	bool TentativeActive() const noexcept;
	int TentativeSteps() const noexcept;

	/// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is
	/// called that many times. Similarly for redo.
	bool CanUndo() const noexcept;
	int StartUndo() noexcept;
	Action GetUndoStep() const noexcept;
	void CompletedUndoStep() noexcept;
	bool CanRedo() const noexcept;
	int StartRedo() noexcept;
	Action GetRedoStep() const noexcept;
	void CompletedRedoStep() noexcept;
};

}

#endif