Strings Initial
This commit is contained in:
parent
9cf541a6ba
commit
eb9210de1c
12 changed files with 1602 additions and 3 deletions
59
Strings/public/Logging.hpp
Normal file
59
Strings/public/Logging.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Buffer.hpp"
|
||||
#include "List.hpp"
|
||||
#include "Strings.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
class Logger {
|
||||
public:
|
||||
|
||||
class Report {
|
||||
String mData;
|
||||
Buffer<ualni> mLineOffsets;
|
||||
|
||||
public:
|
||||
enum Type { INFO, ERR, WARN, SUC } mType = INFO;
|
||||
|
||||
public:
|
||||
Report();
|
||||
explicit Report(const String& text);
|
||||
Report(const String& text, Type type);
|
||||
|
||||
public:
|
||||
[[nodiscard]] ualni getLineCount() const;
|
||||
[[nodiscard]] String getString() const;
|
||||
|
||||
private:
|
||||
void calcLineCount();
|
||||
};
|
||||
|
||||
private:
|
||||
List<Report>::Node* mCursor = nullptr;
|
||||
List<Report> mBuff;
|
||||
ualni mLineCount = 0;
|
||||
|
||||
public:
|
||||
void write(const String& in, bool post = true, Report::Type type = Report::Type::INFO);
|
||||
|
||||
[[nodiscard]] String read();
|
||||
[[nodiscard]] const List<Report>& getBuff() const { return mBuff; }
|
||||
[[nodiscard]] ualni getLineCount() const { return mLineCount; }
|
||||
|
||||
public:
|
||||
static void initializeGlobal();
|
||||
static void deinitializeGlobal();
|
||||
};
|
||||
|
||||
extern Logger* gLogger;
|
||||
}
|
||||
|
||||
#define LOG(val) tp::str::gLogger->write((val), 1)
|
||||
|
||||
#ifdef ENV_BUILD_DEBUG
|
||||
#define DEBUG_LOG(val) tp::str::gLogger->write((val), 1)
|
||||
#else
|
||||
#define DEBUG_LOG(val) (0)
|
||||
#endif
|
||||
174
Strings/public/StringLogic.hpp
Normal file
174
Strings/public/StringLogic.hpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
#pragma once
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
template<typename tChar = uint1>
|
||||
class StringLogic {
|
||||
public:
|
||||
|
||||
typedef halni Index;
|
||||
typedef Index LineId;
|
||||
typedef Index ColumnId;
|
||||
|
||||
static inline bool isNewLineChar(tChar in) { return (in == '\n') || (in == '\r'); }
|
||||
static inline bool isEndChar(tChar in) { return in == '\0'; }
|
||||
static constexpr tChar getEndChar() { return '\0'; }
|
||||
|
||||
static ualni calcLength(const tChar* in) {
|
||||
const tChar* iter = in;
|
||||
while (*iter != '\0') iter++;
|
||||
return iter - in;
|
||||
}
|
||||
|
||||
static tChar* insertLogic(const tChar* cur, const tChar* tar, ualni at, ualni len) {
|
||||
ualni curLen = calcLength(cur);
|
||||
ualni allLen = curLen + len;
|
||||
auto* out = new tChar[allLen + 1];
|
||||
|
||||
DEBUG_ASSERT(curLen >= 0)
|
||||
DEBUG_ASSERT(len >= 0)
|
||||
DEBUG_ASSERT(at < curLen + 1 && at >= 0)
|
||||
|
||||
for (ualni idx = 0; idx < at; idx++) {
|
||||
out[idx] = cur[idx];
|
||||
}
|
||||
for (ualni idx = 0; idx < len; idx++) {
|
||||
out[idx + at] = tar[idx];
|
||||
}
|
||||
for (ualni idx = at + len; idx < allLen; idx++) {
|
||||
out[idx] = cur[idx - len];
|
||||
}
|
||||
out[allLen] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
static tChar* removeLogic(const tChar* cur, ualni start, ualni end) {
|
||||
ualni curLen = calcLength(cur);
|
||||
ualni rangeLen = end - start;
|
||||
ualni newLen = curLen - rangeLen;
|
||||
auto* out = new tChar[newLen + 1];
|
||||
out[newLen] = '\0';
|
||||
for (ualni idx = 0; idx < start; idx++) {
|
||||
out[idx] = cur[idx];
|
||||
}
|
||||
for (ualni idx = end; idx < curLen; idx++) {
|
||||
out[idx - rangeLen] = cur[idx];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool isEqualLogic(const tChar* left, const tChar* right) {
|
||||
ualni idx = 0;
|
||||
LOOP:
|
||||
if (left[idx] == '\0' || right[idx] == '\0') {
|
||||
if (left[idx] == '\0' && right[idx] == '\0') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (left[idx] != right[idx]) {
|
||||
return false;
|
||||
}
|
||||
idx++;
|
||||
goto LOOP;
|
||||
}
|
||||
|
||||
static ualni fromValueLength(alni val, ualni base) {
|
||||
ualni iter = val;
|
||||
ualni len = 0;
|
||||
while (iter /= base) {
|
||||
len++;
|
||||
}
|
||||
bool neg = val < 0;
|
||||
len += 1 + ualni(neg);
|
||||
return len;
|
||||
}
|
||||
|
||||
static tChar* stringFromValue(alni val, tChar* ownBuff, ualni base) {
|
||||
tChar* out;
|
||||
bool neg = val < 0;
|
||||
ualni len = fromValueLength(val, base);
|
||||
out = ownBuff ? ownBuff : new tChar[len + 1];
|
||||
out[len] = '\0';
|
||||
val = abs(val);
|
||||
for (ualni i = len - 1; i >= int(neg); i--) {
|
||||
out[i] = (tChar) (val % base + 48);
|
||||
val /= (alni) base;
|
||||
}
|
||||
if (neg) {
|
||||
out[0] = '-';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static ualni fromValueLength(alnf val, ualni base) {
|
||||
auto rounded = (alni) val;
|
||||
ualni mantissa = ( (alni) val - rounded) * 100000;
|
||||
alni rounded_len = 0;
|
||||
alni mantissa_len = 0;
|
||||
while (rounded /= (alni) base) {
|
||||
rounded_len++;
|
||||
}
|
||||
if (!rounded_len) {
|
||||
rounded_len++;
|
||||
}
|
||||
while (mantissa /= base) {
|
||||
mantissa_len++;
|
||||
}
|
||||
bool neg = val < 0;
|
||||
bool dot = mantissa_len & 1;
|
||||
alni tot_len = mantissa_len + rounded_len + dot + neg;
|
||||
return tot_len;
|
||||
}
|
||||
|
||||
static tChar* fromValue(alnf, ualni, tChar*) {
|
||||
DEBUG_BREAK(false)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static ualni fromValueLength(bool val) {
|
||||
return val ? 4 : 5;
|
||||
}
|
||||
|
||||
static tChar* fromValue(bool val, tChar* ownBuff) {
|
||||
alni len = val ? 4 : 5;
|
||||
tChar* out = ownBuff ? ownBuff : new tChar[len + 1];
|
||||
out[len] = '\0';
|
||||
memCopy(out, val ? "True" : "False", len);
|
||||
return out;
|
||||
}
|
||||
|
||||
static tChar* stringFromValue(int val, ualni base) {
|
||||
return fromValue((alni) val, nullptr, base);
|
||||
}
|
||||
|
||||
static tChar* fromValue(tChar val) {
|
||||
auto* out = new tChar[2];
|
||||
out[1] = '\0';
|
||||
out[0] = val;
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool toValue(const tChar*, alni&, ualni) {
|
||||
DEBUG_BREAK(false)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool toValue(const tChar*, alnf&, ualni) {
|
||||
DEBUG_BREAK(false)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool toValue(const tChar* in, bool& val) {
|
||||
if (!calcLength(in)) return false;
|
||||
if (isEqual(in, "False") || isEqual(in, "false") || isEqual(in, "0")) {
|
||||
val = false;
|
||||
} else {
|
||||
val = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
248
Strings/public/Strings.hpp
Normal file
248
Strings/public/Strings.hpp
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
#pragma once
|
||||
|
||||
#include "PoolAllocator.hpp"
|
||||
#include "StringLogic.hpp"
|
||||
#include "TextEditor.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
// Static data
|
||||
extern ModuleManifest gModuleStrings;
|
||||
|
||||
template<typename tChar>
|
||||
class StringTemplate;
|
||||
using String = StringTemplate<uint1>;
|
||||
|
||||
template<typename tChar = uint1>
|
||||
class StringTemplate {
|
||||
typedef StringLogic<tChar> StringLogic;
|
||||
|
||||
// Hidden slave class
|
||||
class StringData {
|
||||
|
||||
// allows to edit each string with text editor inplace
|
||||
class TextEditors {
|
||||
enum { MAX_EDITED_STRINGS = 65536 }; // 2^16 - do not change mindlessly
|
||||
typedef uint2 Address;
|
||||
ChunkAlloc<TextEditor*, MAX_EDITED_STRINGS> mEditorsPool;
|
||||
|
||||
public:
|
||||
TextEditors() : mEditorsPool() {}
|
||||
|
||||
TextEditor* get(Address address) { return mEditorsPool.getBuff() + address; }
|
||||
|
||||
Address createTextEditor(const tChar* originalBuffer) {
|
||||
auto entry = (TextEditor**) mEditorsPool.allocate(0);
|
||||
*entry = new TextEditor( originalBuffer );
|
||||
return Address(entry - mEditorsPool.getBuff());
|
||||
}
|
||||
|
||||
void destroyTextEditor(Address address) {
|
||||
auto entry = mEditorsPool.getBuff() + address;
|
||||
mEditorsPool.deallocate(entry);
|
||||
delete entry;
|
||||
}
|
||||
};
|
||||
|
||||
static TextEditors sTextEditors;
|
||||
|
||||
public:
|
||||
uint2 mIsConst; // source is non-modifiable
|
||||
uint2 mEditedIdx; // editor id
|
||||
uint4 mReferenceCount; // number of users of this
|
||||
tChar* mBuff; // actual string data
|
||||
|
||||
public:
|
||||
StringData() {
|
||||
MODULE_SANITY_CHECK(gModuleStrings)
|
||||
mBuff = (tChar*) "*";
|
||||
mReferenceCount = 0;
|
||||
mIsConst = true;
|
||||
mEditedIdx = 0;
|
||||
}
|
||||
|
||||
StringData(const tChar* aBuff, bool aIsConst) {
|
||||
MODULE_SANITY_CHECK(gModuleStrings)
|
||||
DEBUG_ASSERT(aBuff)
|
||||
mBuff = (tChar*) aBuff;
|
||||
mReferenceCount = 0;
|
||||
mIsConst = aIsConst;
|
||||
mEditedIdx = 0;
|
||||
}
|
||||
|
||||
explicit StringData(const tChar* aBuff) {
|
||||
MODULE_SANITY_CHECK(gModuleStrings)
|
||||
DEBUG_ASSERT(aBuff)
|
||||
auto const len = StringLogic::calcLength(aBuff);
|
||||
resize(len);
|
||||
memCopy(mBuff, aBuff, len);
|
||||
mReferenceCount = 0;
|
||||
mIsConst = false;
|
||||
mEditedIdx = 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] tChar* getBuffer() {
|
||||
DEBUG_ASSERT(!isEditorMode())
|
||||
if (isEditorMode()) return nullptr;
|
||||
return mBuff;
|
||||
}
|
||||
|
||||
~StringData() {
|
||||
if (!mIsConst) delete[] mBuff;
|
||||
if (isEditorMode()) sTextEditors.destroyTextEditor(mEditedIdx);
|
||||
}
|
||||
|
||||
public:
|
||||
void resize(ualni size) {
|
||||
DEBUG_ASSERT(!isEditorMode() && mReferenceCount == 1 && !mIsConst)
|
||||
delete[] mBuff;
|
||||
mBuff = new tChar[size + 1];
|
||||
mBuff[size] = StringLogic::getEndChar();
|
||||
}
|
||||
|
||||
public:
|
||||
[[nodiscard]] bool isEditorMode() const { return mEditedIdx; }
|
||||
|
||||
void createEditor() {
|
||||
DEBUG_ASSERT(!isEditorMode())
|
||||
if (isEditorMode()) mEditedIdx = sTextEditors.createTextEditor(mBuff);
|
||||
}
|
||||
|
||||
[[nodiscard]] TextEditor* getEditor() const {
|
||||
DEBUG_ASSERT(isEditorMode())
|
||||
if (!isEditorMode()) return nullptr;
|
||||
return sTextEditors.get(mEditedIdx);
|
||||
}
|
||||
|
||||
void applyEditor() {
|
||||
DEBUG_ASSERT(isEditorMode() && mReferenceCount == 1 && !mIsConst)
|
||||
if (!isEditorMode()) return;
|
||||
auto editor = sTextEditors.get(mEditedIdx);
|
||||
resize(editor.textSize());
|
||||
for ( auto character : *editor) {
|
||||
mBuff[character.idx] = character;
|
||||
}
|
||||
}
|
||||
|
||||
void destroyEditor() {
|
||||
DEBUG_ASSERT(isEditorMode())
|
||||
if (!isEditorMode()) return;
|
||||
sTextEditors.destroyTextEditor(mEditedIdx);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
class StringData* mStringData;
|
||||
|
||||
private:
|
||||
enum { STRINGS_POOL_SIZE = 1024, };
|
||||
static PoolAlloc<StringData, STRINGS_POOL_SIZE>* sStringPool;
|
||||
static StringData sNullString;
|
||||
|
||||
// --------------------------------- Main Interface ---------------------------------//
|
||||
public:
|
||||
// Creates null string reference
|
||||
StringTemplate() {
|
||||
mStringData = &sNullString;
|
||||
incReference(mStringData);
|
||||
}
|
||||
|
||||
// References existing string data
|
||||
StringTemplate(const StringTemplate& in) {
|
||||
if (in.isEditorMode()) {
|
||||
// Copies raw data and claims ownership over it
|
||||
mStringData = new (sStringPool->allocate(0)) StringData(in.mStringData->mBuff);
|
||||
incReference(mStringData);
|
||||
} else {
|
||||
mStringData = in.mStringData;
|
||||
incReference(mStringData);
|
||||
}
|
||||
}
|
||||
|
||||
// Creates new string data from raw data pointer.
|
||||
// Does not own string buffer - string buffer will not be freed. Initializes as const memory.
|
||||
explicit StringTemplate(const tChar* raw) {
|
||||
mStringData = new (sStringPool->allocate(0)) StringData(raw, true);
|
||||
incReference(mStringData);
|
||||
}
|
||||
|
||||
// Copies raw data and claims ownership over it
|
||||
explicit StringTemplate(tChar* raw) {
|
||||
mStringData = new (sStringPool->allocate(0)) StringData(raw);
|
||||
incReference(mStringData);
|
||||
}
|
||||
|
||||
~StringTemplate() {
|
||||
decReference(mStringData);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
[[nodiscard]] bool isEditorMode() const {
|
||||
return mStringData->isEditorMode();
|
||||
}
|
||||
|
||||
static void incReference(StringData* dp) {
|
||||
dp->mReferenceCount++;
|
||||
}
|
||||
|
||||
void decReference(StringData* dp) {
|
||||
dp->mReferenceCount--;
|
||||
if (!dp->mReferenceCount) {
|
||||
sStringPool->deallocate(mStringData);
|
||||
mStringData->~StringData();
|
||||
}
|
||||
}
|
||||
|
||||
public: // Access
|
||||
// used to read data
|
||||
[[nodiscard]] const tChar* read() const {
|
||||
return mStringData->mBuff;
|
||||
}
|
||||
|
||||
// used to write data
|
||||
// output will be null if in TextEditing mode
|
||||
[[nodiscard]] tChar* write() {
|
||||
makeModifiable();
|
||||
return mStringData->getBuffer();
|
||||
}
|
||||
|
||||
// output will be null if in TextEditing mode
|
||||
[[nodiscard]] tChar* resize() {
|
||||
if (!mStringData->getEditor()) mStringData->resize();
|
||||
return mStringData->getBuffer();
|
||||
}
|
||||
|
||||
public: // TextEditor Mode
|
||||
// Enters TextEditor Mode
|
||||
void createEditor() { mStringData->createEdited(); }
|
||||
|
||||
// Access to TextEditor Context & Interface
|
||||
[[nodiscard]] TextEditor* getEditor() { return mStringData->getEdited(); }
|
||||
[[nodiscard]] const TextEditor* getEditor() const { return mStringData->getEdited(); }
|
||||
|
||||
// Applies TextEditor Mode To Current buffer
|
||||
void applyEditor() { mStringData->applyEditor(); }
|
||||
|
||||
// Leaves TextEditor Mode
|
||||
void destroyEditor() { mStringData->destroyEditor(); }
|
||||
|
||||
private:
|
||||
void makeModifiable() {
|
||||
// We have no rights to modify if someone references that memory too
|
||||
// So we need to create new copy of StringData
|
||||
if (mStringData->mReferenceCount > 1) {
|
||||
mStringData = new (sStringPool->allocate(0)) StringData(mStringData->mBuff);
|
||||
incReference(mStringData);
|
||||
return;
|
||||
}
|
||||
|
||||
// Also if initial raw data was marked as const - create copy of raw data
|
||||
// Note - raw data will be lost at this point if no other record of such is stored
|
||||
if (mStringData->mIsConst) {
|
||||
mStringData = new (sStringPool->allocate(0)) StringData(mStringData->mBuff);
|
||||
incReference(mStringData);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
382
Strings/public/TextEditor.hpp
Normal file
382
Strings/public/TextEditor.hpp
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Strings.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "Tree.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
class PiecesDS {
|
||||
|
||||
struct Input {
|
||||
const Char* mInput = nullptr;
|
||||
Index mLen = 0;
|
||||
void getLineOffsets(Buffer<Index>& out);
|
||||
};
|
||||
|
||||
struct Pointer {
|
||||
LineId mLine;
|
||||
ColumnId mCol;
|
||||
|
||||
Pointer();
|
||||
Pointer(LineId aLine, ColumnId aCol);
|
||||
Pointer(const Input& aInput);
|
||||
void operator+=(Char achar);
|
||||
void operator+=(const Pointer& ap);
|
||||
Pointer operator-(const Pointer in) const;
|
||||
Pointer operator+(const Pointer& ap) const;
|
||||
bool operator==(const Pointer& in) const;
|
||||
bool operator>(const Pointer& in) const;
|
||||
bool operator>=(const Pointer& in) const;
|
||||
};
|
||||
|
||||
struct Piece {
|
||||
bool mAddedBuff = false;
|
||||
Pointer mStart;
|
||||
Pointer mEnd;
|
||||
|
||||
Pointer diffPtr() const;
|
||||
LineId nLines() const;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
// Tree Node : Based on AvlNodeBlueprint
|
||||
struct Node {
|
||||
|
||||
Node* mLeft = nullptr;
|
||||
Node* mRight = nullptr;
|
||||
Node* mParent = nullptr;
|
||||
halni mHeight = 0;
|
||||
|
||||
Pointer mPtrLeft;
|
||||
Pointer mPtrRight;
|
||||
Pointer mDiff;
|
||||
|
||||
Node* mNext = nullptr;
|
||||
Node* mPrev = nullptr;
|
||||
Piece* mPiece = nullptr;
|
||||
|
||||
static bool mInsertionOperation;
|
||||
|
||||
Node() {}
|
||||
Node(Pointer aKey) {
|
||||
//mVal = aKey;
|
||||
}
|
||||
|
||||
bool disentRight(Pointer aKey) const {
|
||||
return aKey > mPtrLeft;
|
||||
}
|
||||
|
||||
bool disentLeft(Pointer aKey) const {
|
||||
return mPtrLeft > aKey;
|
||||
}
|
||||
|
||||
bool exactNode(Pointer aKey) const {
|
||||
if (mInsertionOperation) {
|
||||
return aKey > mPtrLeft && mPtrLeft + mDiff > aKey;
|
||||
}
|
||||
return aKey >= mPtrLeft && mPtrLeft + mDiff > aKey;
|
||||
}
|
||||
|
||||
Pointer getFindKey(Node* node = nullptr) const {
|
||||
// node must be ancestor of this node
|
||||
|
||||
Pointer out = mPtrLeft;
|
||||
|
||||
if (node == this) {
|
||||
return out;
|
||||
}
|
||||
|
||||
auto iter = this;
|
||||
while (iter->mParent != node) {
|
||||
if (iter == iter->mParent->mRight) {
|
||||
auto left_sub_tree = iter->mParent->mPtrLeft + iter->mParent->mDiff;
|
||||
out = left_sub_tree + out;
|
||||
}
|
||||
iter = iter->mParent;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
if (iter == iter->mParent->mRight) {
|
||||
auto left_sub_tree = iter->mParent->mPtrLeft + iter->mParent->mDiff;
|
||||
out = left_sub_tree + out;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void copyData(const Node& in) {
|
||||
mPiece = in.mPiece;
|
||||
mNext = in.mNext;
|
||||
mPrev = in.mPrev;
|
||||
|
||||
if (mPrev) {
|
||||
mPrev->mNext = this;
|
||||
}
|
||||
|
||||
if (mNext) {
|
||||
mNext->mPrev = this;
|
||||
}
|
||||
}
|
||||
|
||||
Pointer keyInRightSubtree(Pointer key) const { return key - (mPtrLeft + mDiff); }
|
||||
Pointer keyInLeftSubtree(Pointer key) const { return key; }
|
||||
|
||||
void updateTreeCahceCallBack() {
|
||||
DEBUG_ASSERT(mPiece);
|
||||
mDiff = mPiece->diffPtr();
|
||||
if (mLeft) {
|
||||
mPtrLeft = mLeft->sum();
|
||||
}
|
||||
else {
|
||||
mPtrLeft = { 0, 0 };
|
||||
}
|
||||
|
||||
if (mRight) {
|
||||
mPtrRight = mRight->sum();
|
||||
}
|
||||
else {
|
||||
mPtrRight = { 0, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
Pointer sum() const {
|
||||
return (mPtrLeft + mDiff) + mPtrRight;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
AvlTree<Node, Pointer> mTree;
|
||||
|
||||
public:
|
||||
|
||||
PiecesDS() {
|
||||
}
|
||||
|
||||
Node* find(Pointer ptr) const {
|
||||
return mTree.find(ptr);
|
||||
}
|
||||
|
||||
Node* minNode(Node* node) const {
|
||||
return mTree.minNode(node);
|
||||
}
|
||||
|
||||
Node* maxNode(Node* node) const {
|
||||
return mTree.maxNode(node);
|
||||
}
|
||||
|
||||
Node* head() const {
|
||||
return mTree.head();
|
||||
}
|
||||
|
||||
void insert(const Node& node, Pointer ptr) {
|
||||
Node::mInsertionOperation = true;
|
||||
mTree.insert(ptr, node);
|
||||
Node::mInsertionOperation = false;
|
||||
}
|
||||
|
||||
Node* insertAfter(Node* node, Piece piece) {
|
||||
auto key = node ? (node->getFindKey() + node->mDiff) : Pointer{ 0, 0 };
|
||||
auto new_node = Node();
|
||||
new_node.mPiece = new Piece(piece);
|
||||
|
||||
insert(new_node, key);
|
||||
|
||||
#ifdef _DEBUG
|
||||
auto err_node = mTree.isInvalid(mTree.head());
|
||||
DBG_BREAK(err_node);
|
||||
#endif
|
||||
|
||||
auto out = mTree.find(key);
|
||||
|
||||
Node* next = out->mRight;
|
||||
Node* prev = out->mLeft;
|
||||
|
||||
if (!(next || prev) && out->mParent) {
|
||||
if (out->mParent->mLeft == out) {
|
||||
next = out->mParent;
|
||||
}
|
||||
else {
|
||||
prev = out->mParent;
|
||||
}
|
||||
}
|
||||
|
||||
if (next) {
|
||||
out->mNext = next;
|
||||
out->mPrev = next->mPrev;
|
||||
next->mPrev = out;
|
||||
if (out->mPrev) out->mPrev->mNext = out;
|
||||
}
|
||||
else if (prev) {
|
||||
out->mPrev = prev;
|
||||
out->mNext = prev->mNext;
|
||||
prev->mNext = out;
|
||||
if (out->mNext) out->mNext->mPrev = out;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void remove(Node* aNode) {
|
||||
auto key = aNode->getFindKey();
|
||||
|
||||
if (aNode->mPrev) {
|
||||
aNode->mPrev->mNext = aNode->mNext;
|
||||
}
|
||||
|
||||
if (aNode->mNext) {
|
||||
aNode->mNext->mPrev = aNode->mPrev;
|
||||
}
|
||||
|
||||
auto del = aNode->mPiece;
|
||||
mTree.remove(key);
|
||||
delete del;
|
||||
|
||||
#ifdef _DEBUG
|
||||
auto err_node = mTree.isInvalid(mTree.head());
|
||||
DBG_BREAK(err_node);
|
||||
#endif
|
||||
}
|
||||
|
||||
Node* first() const {
|
||||
return mTree.minNode(mTree.head());
|
||||
}
|
||||
|
||||
Pointer ptrSum() const {
|
||||
if (mTree.head()) {
|
||||
return mTree.head()->sum();
|
||||
}
|
||||
return { 0, 0 };
|
||||
}
|
||||
|
||||
halni nPieces() const {
|
||||
return mTree.size();
|
||||
}
|
||||
|
||||
void update_cache(Node* node) {
|
||||
if (node) {
|
||||
node->updateTreeCahceCallBack();
|
||||
update_cache(node->mParent);
|
||||
}
|
||||
}
|
||||
|
||||
void undo() {}
|
||||
void redo() {}
|
||||
void pushUndoPoint() {}
|
||||
};
|
||||
|
||||
struct AddBuff {
|
||||
Buffer<Char> mBuff;
|
||||
Buffer<Index> mLineOffsets;
|
||||
Index mColumn = 0;
|
||||
|
||||
AddBuff();
|
||||
void append(Input aInput);
|
||||
Pointer endPtr();
|
||||
};
|
||||
|
||||
struct OrigBuff {
|
||||
bool mOwnsInput = false;
|
||||
Input mOriginal;
|
||||
Buffer<Index> mLineOffsets;
|
||||
|
||||
Pointer mPtrStart;
|
||||
Pointer mPtrEnd;
|
||||
|
||||
// does not owns the original input by default
|
||||
OrigBuff(Input aOriginal);
|
||||
|
||||
void makeOwnOriginalInput();
|
||||
|
||||
~OrigBuff();
|
||||
};
|
||||
|
||||
struct PieceCrs {
|
||||
Pointer piece_ptr;
|
||||
Pointer offset;
|
||||
Pointer ptr;
|
||||
PiecesDS::Node* piece = nullptr;
|
||||
};
|
||||
|
||||
class TextEditor {
|
||||
friend class Iterator;
|
||||
|
||||
class Iterator {
|
||||
friend TextEditor;
|
||||
|
||||
const TextEditor* mSelf;
|
||||
PiecesDS::Node* mNode = nullptr;
|
||||
Pointer mOffset = { 0, 0 };
|
||||
Pointer mPtr = { 0, 0 };
|
||||
Char mVal = 0;
|
||||
|
||||
Iterator(const TextEditor* mSelf);
|
||||
bool inputLeft();
|
||||
|
||||
public:
|
||||
|
||||
Char character();
|
||||
void operator++();
|
||||
bool operator!=(alni const& iter);
|
||||
bool operator==(alni const& iter);
|
||||
Char operator->();
|
||||
const Iterator& operator*();
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
// pieces of strings that construct the text
|
||||
PiecesDS mPieces;
|
||||
|
||||
// string buffers to wich pieces can reffer
|
||||
OrigBuff mOrigBuff;
|
||||
AddBuff mAddBuff;
|
||||
|
||||
// active position on the text
|
||||
Pointer mCursor;
|
||||
Pointer mCursorEnd;
|
||||
|
||||
Pointer mLastInsertionCrs;
|
||||
//private:
|
||||
|
||||
[[nodiscard]] PieceCrs findPiece(Pointer ptr) const;
|
||||
[[nodiscard]] Pointer prevPiecePtr(PiecesDS::Node* piece_node, Pointer piece_pointer) const;
|
||||
[[nodiscard]] Pointer nextPiecePtr(PiecesDS::Node* piece_node, Pointer piece_pointer) const;
|
||||
|
||||
[[nodiscard]] const Char* getBuffLine(const Piece*, LineId) const;
|
||||
[[nodiscard]] const Char* getPieceLine(const Piece*, LineId) const;
|
||||
[[nodiscard]] PiecesDS::Node* splitPiece(PiecesDS::Node* piece, Pointer split_ptr);
|
||||
[[nodiscard]] LineId getPieceLineLen(Piece* aPiece, LineId line) const;
|
||||
|
||||
public:
|
||||
enum Dir : uint1 { LEFT, RIGHT, UP, DOWN };
|
||||
enum DirUnit : uint1 { CHAR, WORD, TOK, LINE_END, LINE_START };
|
||||
|
||||
public:
|
||||
|
||||
TextEditor();
|
||||
explicit TextEditor(Input original);
|
||||
|
||||
[[nodiscard]] Index mNLines() const;
|
||||
void clampPtr(Pointer& ptr) const;
|
||||
[[nodiscard]] Pointer getCursor() const;
|
||||
[[nodiscard]] Pointer getCursorEnd() const;
|
||||
void setCursor(Pointer ptr);
|
||||
void setCursorEnd(Pointer ptr);
|
||||
|
||||
void insert(Input aInput);
|
||||
|
||||
void remove(bool only_selection = false, Dir dir = LEFT, DirUnit unit = CHAR);
|
||||
|
||||
void undo();
|
||||
void redo();
|
||||
|
||||
Iterator begin();
|
||||
alni end();
|
||||
|
||||
~TextEditor();
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue