From 4c51efc5e07e52b6ad7aa42a738c3cc6c4fdd84d Mon Sep 17 00:00:00 2001 From: WerWolv Date: Wed, 2 Feb 2022 16:03:18 +0100 Subject: [PATCH] pattern: Improved parsing of integer literals, added support for hex floats --- lib/external/imgui/source/TextEditor.cpp | 5139 ++++++++--------- .../source/pattern_language/lexer.cpp | 181 +- main/source/init/tasks.cpp | 2 + .../content/views/view_pattern_editor.cpp | 2 +- 4 files changed, 2506 insertions(+), 2818 deletions(-) diff --git a/lib/external/imgui/source/TextEditor.cpp b/lib/external/imgui/source/TextEditor.cpp index cf5ebfbbc..18761b3f4 100644 --- a/lib/external/imgui/source/TextEditor.cpp +++ b/lib/external/imgui/source/TextEditor.cpp @@ -7,3172 +7,2869 @@ #include "TextEditor.h" #define IMGUI_DEFINE_MATH_OPERATORS -#include "imgui.h" // for imGui::GetCurrentWindow() +#include "imgui.h" // for imGui::GetCurrentWindow() // TODO // - multiline comments vs single-line: latter is blocking start of a ML template -bool equals(InputIt1 first1, InputIt1 last1, - InputIt2 first2, InputIt2 last2, BinaryPredicate p) -{ - for (; first1 != last1 && first2 != last2; ++first1, ++first2) - { - if (!p(*first1, *first2)) - return false; - } - return first1 == last1 && first2 == last2; +bool equals(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p) { + for (; first1 != last1 && first2 != last2; ++first1, ++first2) { + if (!p(*first1, *first2)) + return false; + } + return first1 == last1 && first2 == last2; } TextEditor::TextEditor() - : mLineSpacing(1.0f) - , mUndoIndex(0) - , mTabSize(4) - , mOverwrite(false) - , mReadOnly(false) - , mWithinRender(false) - , mScrollToCursor(false) - , mScrollToTop(false) - , mTextChanged(false) - , mColorizerEnabled(true) - , mTextStart(20.0f) - , mLeftMargin(10) - , mCursorPositionChanged(false) - , mColorRangeMin(0) - , mColorRangeMax(0) - , mSelectionMode(SelectionMode::Normal) - , mCheckComments(true) - , mLastClick(-1.0f) - , mHandleKeyboardInputs(true) - , mHandleMouseInputs(true) - , mIgnoreImGuiChild(false) - , mShowWhitespaces(true) - , mStartTime(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) -{ - SetPalette(GetDarkPalette()); - SetLanguageDefinition(LanguageDefinition::HLSL()); - mLines.push_back(Line()); + : mLineSpacing(1.0f), mUndoIndex(0), mTabSize(4), mOverwrite(false), mReadOnly(false), mWithinRender(false), mScrollToCursor(false), mScrollToTop(false), mTextChanged(false), mColorizerEnabled(true), mTextStart(20.0f), mLeftMargin(10), mCursorPositionChanged(false), mColorRangeMin(0), mColorRangeMax(0), mSelectionMode(SelectionMode::Normal), mCheckComments(true), mLastClick(-1.0f), mHandleKeyboardInputs(true), mHandleMouseInputs(true), mIgnoreImGuiChild(false), mShowWhitespaces(true), mStartTime(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) { + SetPalette(GetDarkPalette()); + SetLanguageDefinition(LanguageDefinition::HLSL()); + mLines.push_back(Line()); } -TextEditor::~TextEditor() -{ +TextEditor::~TextEditor() { } -void TextEditor::SetLanguageDefinition(const LanguageDefinition & aLanguageDef) -{ - mLanguageDefinition = aLanguageDef; - mRegexList.clear(); +void TextEditor::SetLanguageDefinition(const LanguageDefinition &aLanguageDef) { + mLanguageDefinition = aLanguageDef; + mRegexList.clear(); - for (auto& r : mLanguageDefinition.mTokenRegexStrings) - mRegexList.push_back(std::make_pair(std::regex(r.first, std::regex_constants::optimize), r.second)); + for (auto &r : mLanguageDefinition.mTokenRegexStrings) + mRegexList.push_back(std::make_pair(std::regex(r.first, std::regex_constants::optimize), r.second)); - Colorize(); + Colorize(); } -void TextEditor::SetPalette(const Palette & aValue) -{ - mPaletteBase = aValue; +void TextEditor::SetPalette(const Palette &aValue) { + mPaletteBase = aValue; } -std::string TextEditor::GetText(const Coordinates & aStart, const Coordinates & aEnd) const -{ - std::string result; +std::string TextEditor::GetText(const Coordinates &aStart, const Coordinates &aEnd) const { + std::string result; - auto lstart = aStart.mLine; - auto lend = aEnd.mLine; - auto istart = GetCharacterIndex(aStart); - auto iend = GetCharacterIndex(aEnd); - size_t s = 0; + auto lstart = aStart.mLine; + auto lend = aEnd.mLine; + auto istart = GetCharacterIndex(aStart); + auto iend = GetCharacterIndex(aEnd); + size_t s = 0; - for (size_t i = lstart; i < lend; i++) - s += mLines[i].size(); + for (size_t i = lstart; i < lend; i++) + s += mLines[i].size(); - result.reserve(s + s / 8); + result.reserve(s + s / 8); - while (istart < iend || lstart < lend) - { - if (lstart >= (int)mLines.size()) - break; + while (istart < iend || lstart < lend) { + if (lstart >= (int)mLines.size()) + break; - auto& line = mLines[lstart]; - if (istart < (int)line.size()) - { - result += line[istart].mChar; - istart++; - } - else - { - istart = 0; - ++lstart; - result += '\n'; - } - } + auto &line = mLines[lstart]; + if (istart < (int)line.size()) { + result += line[istart].mChar; + istart++; + } else { + istart = 0; + ++lstart; + result += '\n'; + } + } - return result; + return result; } -TextEditor::Coordinates TextEditor::GetActualCursorCoordinates() const -{ - return SanitizeCoordinates(mState.mCursorPosition); +TextEditor::Coordinates TextEditor::GetActualCursorCoordinates() const { + return SanitizeCoordinates(mState.mCursorPosition); } -TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates & aValue) const -{ - auto line = aValue.mLine; - auto column = aValue.mColumn; - if (line >= (int)mLines.size()) - { - if (mLines.empty()) - { - line = 0; - column = 0; - } - else - { - line = (int)mLines.size() - 1; - column = GetLineMaxColumn(line); - } - return Coordinates(line, column); - } - else - { - column = mLines.empty() ? 0 : std::min(column, GetLineMaxColumn(line)); - return Coordinates(line, column); - } +TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates &aValue) const { + auto line = aValue.mLine; + auto column = aValue.mColumn; + if (line >= (int)mLines.size()) { + if (mLines.empty()) { + line = 0; + column = 0; + } else { + line = (int)mLines.size() - 1; + column = GetLineMaxColumn(line); + } + return Coordinates(line, column); + } else { + column = mLines.empty() ? 0 : std::min(column, GetLineMaxColumn(line)); + return Coordinates(line, column); + } } // https://en.wikipedia.org/wiki/UTF-8 // We assume that the char is a standalone character (<128) or a leading byte of an UTF-8 code sequence (non-10xxxxxx code) -static int UTF8CharLength(TextEditor::Char c) -{ - if ((c & 0xFE) == 0xFC) - return 6; - if ((c & 0xFC) == 0xF8) - return 5; - if ((c & 0xF8) == 0xF0) - return 4; - else if ((c & 0xF0) == 0xE0) - return 3; - else if ((c & 0xE0) == 0xC0) - return 2; - return 1; +static int UTF8CharLength(TextEditor::Char c) { + if ((c & 0xFE) == 0xFC) + return 6; + if ((c & 0xFC) == 0xF8) + return 5; + if ((c & 0xF8) == 0xF0) + return 4; + else if ((c & 0xF0) == 0xE0) + return 3; + else if ((c & 0xE0) == 0xC0) + return 2; + return 1; } // "Borrowed" from ImGui source -static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) -{ - if (c < 0x80) - { - buf[0] = (char)c; - return 1; - } - if (c < 0x800) - { - if (buf_size < 2) return 0; - buf[0] = (char)(0xc0 + (c >> 6)); - buf[1] = (char)(0x80 + (c & 0x3f)); - return 2; - } - if (c >= 0xdc00 && c < 0xe000) - { - return 0; - } - if (c >= 0xd800 && c < 0xdc00) - { - if (buf_size < 4) return 0; - buf[0] = (char)(0xf0 + (c >> 18)); - buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); - buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); - buf[3] = (char)(0x80 + ((c) & 0x3f)); - return 4; - } - //else if (c < 0x10000) - { - if (buf_size < 3) return 0; - buf[0] = (char)(0xe0 + (c >> 12)); - buf[1] = (char)(0x80 + ((c >> 6) & 0x3f)); - buf[2] = (char)(0x80 + ((c) & 0x3f)); - return 3; - } +static inline int ImTextCharToUtf8(char *buf, int buf_size, unsigned int c) { + if (c < 0x80) { + buf[0] = (char)c; + return 1; + } + if (c < 0x800) { + if (buf_size < 2) return 0; + buf[0] = (char)(0xc0 + (c >> 6)); + buf[1] = (char)(0x80 + (c & 0x3f)); + return 2; + } + if (c >= 0xdc00 && c < 0xe000) { + return 0; + } + if (c >= 0xd800 && c < 0xdc00) { + if (buf_size < 4) return 0; + buf[0] = (char)(0xf0 + (c >> 18)); + buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); + buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); + buf[3] = (char)(0x80 + ((c)&0x3f)); + return 4; + } + // else if (c < 0x10000) + { + if (buf_size < 3) return 0; + buf[0] = (char)(0xe0 + (c >> 12)); + buf[1] = (char)(0x80 + ((c >> 6) & 0x3f)); + buf[2] = (char)(0x80 + ((c)&0x3f)); + return 3; + } } -void TextEditor::Advance(Coordinates & aCoordinates) const -{ - if (aCoordinates.mLine < (int)mLines.size()) - { - auto& line = mLines[aCoordinates.mLine]; - auto cindex = GetCharacterIndex(aCoordinates); +void TextEditor::Advance(Coordinates &aCoordinates) const { + if (aCoordinates.mLine < (int)mLines.size()) { + auto &line = mLines[aCoordinates.mLine]; + auto cindex = GetCharacterIndex(aCoordinates); - if (cindex + 1 < (int)line.size()) - { - auto delta = UTF8CharLength(line[cindex].mChar); - cindex = std::min(cindex + delta, (int)line.size() - 1); - } - else - { - ++aCoordinates.mLine; - cindex = 0; - } - aCoordinates.mColumn = GetCharacterColumn(aCoordinates.mLine, cindex); - } + if (cindex + 1 < (int)line.size()) { + auto delta = UTF8CharLength(line[cindex].mChar); + cindex = std::min(cindex + delta, (int)line.size() - 1); + } else { + ++aCoordinates.mLine; + cindex = 0; + } + aCoordinates.mColumn = GetCharacterColumn(aCoordinates.mLine, cindex); + } } -void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEnd) -{ - assert(aEnd >= aStart); - assert(!mReadOnly); +void TextEditor::DeleteRange(const Coordinates &aStart, const Coordinates &aEnd) { + assert(aEnd >= aStart); + assert(!mReadOnly); - //printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine, aEnd.mColumn); + // printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine, aEnd.mColumn); - if (aEnd == aStart) - return; + if (aEnd == aStart) + return; - auto start = GetCharacterIndex(aStart); - auto end = GetCharacterIndex(aEnd); + auto start = GetCharacterIndex(aStart); + auto end = GetCharacterIndex(aEnd); - if (aStart.mLine == aEnd.mLine) - { - auto& line = mLines[aStart.mLine]; - auto n = GetLineMaxColumn(aStart.mLine); - if (aEnd.mColumn >= n) - line.erase(line.begin() + start, line.end()); - else - line.erase(line.begin() + start, line.begin() + end); - } - else - { - auto& firstLine = mLines[aStart.mLine]; - auto& lastLine = mLines[aEnd.mLine]; + if (aStart.mLine == aEnd.mLine) { + auto &line = mLines[aStart.mLine]; + auto n = GetLineMaxColumn(aStart.mLine); + if (aEnd.mColumn >= n) + line.erase(line.begin() + start, line.end()); + else + line.erase(line.begin() + start, line.begin() + end); + } else { + auto &firstLine = mLines[aStart.mLine]; + auto &lastLine = mLines[aEnd.mLine]; - firstLine.erase(firstLine.begin() + start, firstLine.end()); - lastLine.erase(lastLine.begin(), lastLine.begin() + end); + firstLine.erase(firstLine.begin() + start, firstLine.end()); + lastLine.erase(lastLine.begin(), lastLine.begin() + end); - if (aStart.mLine < aEnd.mLine) - firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end()); + if (aStart.mLine < aEnd.mLine) + firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end()); - if (aStart.mLine < aEnd.mLine) - RemoveLine(aStart.mLine + 1, aEnd.mLine + 1); - } + if (aStart.mLine < aEnd.mLine) + RemoveLine(aStart.mLine + 1, aEnd.mLine + 1); + } - mTextChanged = true; + mTextChanged = true; } -int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValue) -{ - assert(!mReadOnly); +int TextEditor::InsertTextAt(Coordinates & /* inout */ aWhere, const char *aValue) { + assert(!mReadOnly); - int cindex = GetCharacterIndex(aWhere); - int totalLines = 0; - while (*aValue != '\0') - { - assert(!mLines.empty()); + int cindex = GetCharacterIndex(aWhere); + int totalLines = 0; + while (*aValue != '\0') { + assert(!mLines.empty()); - if (*aValue == '\r') - { - // skip - ++aValue; - } - else if (*aValue == '\n') - { - if (cindex < (int)mLines[aWhere.mLine].size()) - { - auto& newLine = InsertLine(aWhere.mLine + 1); - auto& line = mLines[aWhere.mLine]; - newLine.insert(newLine.begin(), line.begin() + cindex, line.end()); - line.erase(line.begin() + cindex, line.end()); - } - else - { - InsertLine(aWhere.mLine + 1); - } - ++aWhere.mLine; - aWhere.mColumn = 0; - cindex = 0; - ++totalLines; - ++aValue; - } - else - { - auto& line = mLines[aWhere.mLine]; - auto d = UTF8CharLength(*aValue); - while (d-- > 0 && *aValue != '\0') - line.insert(line.begin() + cindex++, Glyph(*aValue++, PaletteIndex::Default)); - ++aWhere.mColumn; - } + if (*aValue == '\r') { + // skip + ++aValue; + } else if (*aValue == '\n') { + if (cindex < (int)mLines[aWhere.mLine].size()) { + auto &newLine = InsertLine(aWhere.mLine + 1); + auto &line = mLines[aWhere.mLine]; + newLine.insert(newLine.begin(), line.begin() + cindex, line.end()); + line.erase(line.begin() + cindex, line.end()); + } else { + InsertLine(aWhere.mLine + 1); + } + ++aWhere.mLine; + aWhere.mColumn = 0; + cindex = 0; + ++totalLines; + ++aValue; + } else { + auto &line = mLines[aWhere.mLine]; + auto d = UTF8CharLength(*aValue); + while (d-- > 0 && *aValue != '\0') + line.insert(line.begin() + cindex++, Glyph(*aValue++, PaletteIndex::Default)); + ++aWhere.mColumn; + } - mTextChanged = true; - } + mTextChanged = true; + } - return totalLines; + return totalLines; } -void TextEditor::AddUndo(UndoRecord& aValue) -{ - assert(!mReadOnly); - //printf("AddUndo: (@%d.%d) +\'%s' [%d.%d .. %d.%d], -\'%s', [%d.%d .. %d.%d] (@%d.%d)\n", - // aValue.mBefore.mCursorPosition.mLine, aValue.mBefore.mCursorPosition.mColumn, - // aValue.mAdded.c_str(), aValue.mAddedStart.mLine, aValue.mAddedStart.mColumn, aValue.mAddedEnd.mLine, aValue.mAddedEnd.mColumn, - // aValue.mRemoved.c_str(), aValue.mRemovedStart.mLine, aValue.mRemovedStart.mColumn, aValue.mRemovedEnd.mLine, aValue.mRemovedEnd.mColumn, - // aValue.mAfter.mCursorPosition.mLine, aValue.mAfter.mCursorPosition.mColumn - // ); +void TextEditor::AddUndo(UndoRecord &aValue) { + assert(!mReadOnly); + // printf("AddUndo: (@%d.%d) +\'%s' [%d.%d .. %d.%d], -\'%s', [%d.%d .. %d.%d] (@%d.%d)\n", + // aValue.mBefore.mCursorPosition.mLine, aValue.mBefore.mCursorPosition.mColumn, + // aValue.mAdded.c_str(), aValue.mAddedStart.mLine, aValue.mAddedStart.mColumn, aValue.mAddedEnd.mLine, aValue.mAddedEnd.mColumn, + // aValue.mRemoved.c_str(), aValue.mRemovedStart.mLine, aValue.mRemovedStart.mColumn, aValue.mRemovedEnd.mLine, aValue.mRemovedEnd.mColumn, + // aValue.mAfter.mCursorPosition.mLine, aValue.mAfter.mCursorPosition.mColumn + // ); - mUndoBuffer.resize((size_t)(mUndoIndex + 1)); - mUndoBuffer.back() = aValue; - ++mUndoIndex; + mUndoBuffer.resize((size_t)(mUndoIndex + 1)); + mUndoBuffer.back() = aValue; + ++mUndoIndex; } -TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPosition) const -{ - ImVec2 origin = ImGui::GetCursorScreenPos(); - ImVec2 local(aPosition.x - origin.x, aPosition.y - origin.y); +TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2 &aPosition) const { + ImVec2 origin = ImGui::GetCursorScreenPos(); + ImVec2 local(aPosition.x - origin.x, aPosition.y - origin.y); - int lineNo = std::max(0, (int)floor(local.y / mCharAdvance.y)); + int lineNo = std::max(0, (int)floor(local.y / mCharAdvance.y)); - int columnCoord = 0; + int columnCoord = 0; - if (lineNo >= 0 && lineNo < (int)mLines.size()) - { - auto& line = mLines.at(lineNo); + if (lineNo >= 0 && lineNo < (int)mLines.size()) { + auto &line = mLines.at(lineNo); - int columnIndex = 0; - float columnX = 0.0f; + int columnIndex = 0; + float columnX = 0.0f; - while ((size_t)columnIndex < line.size()) - { - float columnWidth = 0.0f; + while ((size_t)columnIndex < line.size()) { + float columnWidth = 0.0f; - if (line[columnIndex].mChar == '\t') - { - float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ").x; - float oldX = columnX; - float newColumnX = (1.0f + std::floor((1.0f + columnX) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); - columnWidth = newColumnX - oldX; - if (mTextStart + columnX + columnWidth * 0.5f > local.x) - break; - columnX = newColumnX; - columnCoord = (columnCoord / mTabSize) * mTabSize + mTabSize; - columnIndex++; - } - else - { - char buf[7]; - auto d = UTF8CharLength(line[columnIndex].mChar); - int i = 0; - while (i < 6 && d-- > 0) - buf[i++] = line[columnIndex++].mChar; - buf[i] = '\0'; - columnWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x; - if (mTextStart + columnX + columnWidth * 0.5f > local.x) - break; - columnX += columnWidth; - columnCoord++; - } - } - } + if (line[columnIndex].mChar == '\t') { + float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ").x; + float oldX = columnX; + float newColumnX = (1.0f + std::floor((1.0f + columnX) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); + columnWidth = newColumnX - oldX; + if (mTextStart + columnX + columnWidth * 0.5f > local.x) + break; + columnX = newColumnX; + columnCoord = (columnCoord / mTabSize) * mTabSize + mTabSize; + columnIndex++; + } else { + char buf[7]; + auto d = UTF8CharLength(line[columnIndex].mChar); + int i = 0; + while (i < 6 && d-- > 0) + buf[i++] = line[columnIndex++].mChar; + buf[i] = '\0'; + columnWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x; + if (mTextStart + columnX + columnWidth * 0.5f > local.x) + break; + columnX += columnWidth; + columnCoord++; + } + } + } - return SanitizeCoordinates(Coordinates(lineNo, columnCoord)); + return SanitizeCoordinates(Coordinates(lineNo, columnCoord)); } -TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates & aFrom) const -{ - Coordinates at = aFrom; - if (at.mLine >= (int)mLines.size()) - return at; +TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates &aFrom) const { + Coordinates at = aFrom; + if (at.mLine >= (int)mLines.size()) + return at; - auto& line = mLines[at.mLine]; - auto cindex = GetCharacterIndex(at); + auto &line = mLines[at.mLine]; + auto cindex = GetCharacterIndex(at); - if (cindex >= (int)line.size()) - return at; + if (cindex >= (int)line.size()) + return at; - while (cindex > 0 && isspace(line[cindex].mChar)) - --cindex; + while (cindex > 0 && isspace(line[cindex].mChar)) + --cindex; - auto cstart = (PaletteIndex)line[cindex].mColorIndex; - while (cindex > 0) - { - auto c = line[cindex].mChar; - if ((c & 0xC0) != 0x80) // not UTF code sequence 10xxxxxx - { - if (c <= 32 && isspace(c)) - { - cindex++; - break; - } - if (cstart != (PaletteIndex)line[size_t(cindex - 1)].mColorIndex) - break; - } - --cindex; - } - return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex)); + auto cstart = (PaletteIndex)line[cindex].mColorIndex; + while (cindex > 0) { + auto c = line[cindex].mChar; + if ((c & 0xC0) != 0x80) // not UTF code sequence 10xxxxxx + { + if (c <= 32 && isspace(c)) { + cindex++; + break; + } + if (cstart != (PaletteIndex)line[size_t(cindex - 1)].mColorIndex) + break; + } + --cindex; + } + return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex)); } -TextEditor::Coordinates TextEditor::FindWordEnd(const Coordinates & aFrom) const -{ - Coordinates at = aFrom; - if (at.mLine >= (int)mLines.size()) - return at; +TextEditor::Coordinates TextEditor::FindWordEnd(const Coordinates &aFrom) const { + Coordinates at = aFrom; + if (at.mLine >= (int)mLines.size()) + return at; - auto& line = mLines[at.mLine]; - auto cindex = GetCharacterIndex(at); + auto &line = mLines[at.mLine]; + auto cindex = GetCharacterIndex(at); - if (cindex >= (int)line.size()) - return at; + if (cindex >= (int)line.size()) + return at; - bool prevspace = (bool)isspace(line[cindex].mChar); - auto cstart = (PaletteIndex)line[cindex].mColorIndex; - while (cindex < (int)line.size()) - { - auto c = line[cindex].mChar; - auto d = UTF8CharLength(c); - if (cstart != (PaletteIndex)line[cindex].mColorIndex) - break; + bool prevspace = (bool)isspace(line[cindex].mChar); + auto cstart = (PaletteIndex)line[cindex].mColorIndex; + while (cindex < (int)line.size()) { + auto c = line[cindex].mChar; + auto d = UTF8CharLength(c); + if (cstart != (PaletteIndex)line[cindex].mColorIndex) + break; - if (prevspace != !!isspace(c)) - { - if (isspace(c)) - while (cindex < (int)line.size() && isspace(line[cindex].mChar)) - ++cindex; - break; - } - cindex += d; - } - return Coordinates(aFrom.mLine, GetCharacterColumn(aFrom.mLine, cindex)); + if (prevspace != !!isspace(c)) { + if (isspace(c)) + while (cindex < (int)line.size() && isspace(line[cindex].mChar)) + ++cindex; + break; + } + cindex += d; + } + return Coordinates(aFrom.mLine, GetCharacterColumn(aFrom.mLine, cindex)); } -TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates & aFrom) const -{ - Coordinates at = aFrom; - if (at.mLine >= (int)mLines.size()) - return at; +TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates &aFrom) const { + Coordinates at = aFrom; + if (at.mLine >= (int)mLines.size()) + return at; - // skip to the next non-word character - auto cindex = GetCharacterIndex(aFrom); - bool isword = false; - bool skip = false; - if (cindex < (int)mLines[at.mLine].size()) - { - auto& line = mLines[at.mLine]; - isword = isalnum(line[cindex].mChar); - skip = isword; - } + // skip to the next non-word character + auto cindex = GetCharacterIndex(aFrom); + bool isword = false; + bool skip = false; + if (cindex < (int)mLines[at.mLine].size()) { + auto &line = mLines[at.mLine]; + isword = isalnum(line[cindex].mChar); + skip = isword; + } - while (!isword || skip) - { - if (at.mLine >= mLines.size()) - { - auto l = std::max(0, (int) mLines.size() - 1); - return Coordinates(l, GetLineMaxColumn(l)); - } + while (!isword || skip) { + if (at.mLine >= mLines.size()) { + auto l = std::max(0, (int)mLines.size() - 1); + return Coordinates(l, GetLineMaxColumn(l)); + } - auto& line = mLines[at.mLine]; - if (cindex < (int)line.size()) - { - isword = isalnum(line[cindex].mChar); + auto &line = mLines[at.mLine]; + if (cindex < (int)line.size()) { + isword = isalnum(line[cindex].mChar); - if (isword && !skip) - return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex)); + if (isword && !skip) + return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex)); - if (!isword) - skip = false; + if (!isword) + skip = false; - cindex++; - } - else - { - cindex = 0; - ++at.mLine; - skip = false; - isword = false; - } - } + cindex++; + } else { + cindex = 0; + ++at.mLine; + skip = false; + isword = false; + } + } - return at; + return at; } -int TextEditor::GetCharacterIndex(const Coordinates& aCoordinates) const -{ - if (aCoordinates.mLine >= mLines.size()) - return -1; - auto& line = mLines[aCoordinates.mLine]; - int c = 0; - int i = 0; - for (; i < line.size() && c < aCoordinates.mColumn;) - { - if (line[i].mChar == '\t') - c = (c / mTabSize) * mTabSize + mTabSize; - else - ++c; - i += UTF8CharLength(line[i].mChar); - } - return i; +int TextEditor::GetCharacterIndex(const Coordinates &aCoordinates) const { + if (aCoordinates.mLine >= mLines.size()) + return -1; + auto &line = mLines[aCoordinates.mLine]; + int c = 0; + int i = 0; + for (; i < line.size() && c < aCoordinates.mColumn;) { + if (line[i].mChar == '\t') + c = (c / mTabSize) * mTabSize + mTabSize; + else + ++c; + i += UTF8CharLength(line[i].mChar); + } + return i; } -int TextEditor::GetCharacterColumn(int aLine, int aIndex) const -{ - if (aLine >= mLines.size()) - return 0; - auto& line = mLines[aLine]; - int col = 0; - int i = 0; - while (i < aIndex && i < (int)line.size()) - { - auto c = line[i].mChar; - i += UTF8CharLength(c); - if (c == '\t') - col = (col / mTabSize) * mTabSize + mTabSize; - else - col++; - } - return col; +int TextEditor::GetCharacterColumn(int aLine, int aIndex) const { + if (aLine >= mLines.size()) + return 0; + auto &line = mLines[aLine]; + int col = 0; + int i = 0; + while (i < aIndex && i < (int)line.size()) { + auto c = line[i].mChar; + i += UTF8CharLength(c); + if (c == '\t') + col = (col / mTabSize) * mTabSize + mTabSize; + else + col++; + } + return col; } -int TextEditor::GetLineCharacterCount(int aLine) const -{ - if (aLine >= mLines.size()) - return 0; - auto& line = mLines[aLine]; - int c = 0; - for (unsigned i = 0; i < line.size(); c++) - i += UTF8CharLength(line[i].mChar); - return c; +int TextEditor::GetLineCharacterCount(int aLine) const { + if (aLine >= mLines.size()) + return 0; + auto &line = mLines[aLine]; + int c = 0; + for (unsigned i = 0; i < line.size(); c++) + i += UTF8CharLength(line[i].mChar); + return c; } -int TextEditor::GetLineMaxColumn(int aLine) const -{ - if (aLine >= mLines.size()) - return 0; - auto& line = mLines[aLine]; - int col = 0; - for (unsigned i = 0; i < line.size(); ) - { - auto c = line[i].mChar; - if (c == '\t') - col = (col / mTabSize) * mTabSize + mTabSize; - else - col++; - i += UTF8CharLength(c); - } - return col; +int TextEditor::GetLineMaxColumn(int aLine) const { + if (aLine >= mLines.size()) + return 0; + auto &line = mLines[aLine]; + int col = 0; + for (unsigned i = 0; i < line.size();) { + auto c = line[i].mChar; + if (c == '\t') + col = (col / mTabSize) * mTabSize + mTabSize; + else + col++; + i += UTF8CharLength(c); + } + return col; } -bool TextEditor::IsOnWordBoundary(const Coordinates & aAt) const -{ - if (aAt.mLine >= (int)mLines.size() || aAt.mColumn == 0) - return true; +bool TextEditor::IsOnWordBoundary(const Coordinates &aAt) const { + if (aAt.mLine >= (int)mLines.size() || aAt.mColumn == 0) + return true; - auto& line = mLines[aAt.mLine]; - auto cindex = GetCharacterIndex(aAt); - if (cindex >= (int)line.size()) - return true; + auto &line = mLines[aAt.mLine]; + auto cindex = GetCharacterIndex(aAt); + if (cindex >= (int)line.size()) + return true; - if (mColorizerEnabled) - return line[cindex].mColorIndex != line[size_t(cindex - 1)].mColorIndex; + if (mColorizerEnabled) + return line[cindex].mColorIndex != line[size_t(cindex - 1)].mColorIndex; - return isspace(line[cindex].mChar) != isspace(line[cindex - 1].mChar); + return isspace(line[cindex].mChar) != isspace(line[cindex - 1].mChar); } -void TextEditor::RemoveLine(int aStart, int aEnd) -{ - assert(!mReadOnly); - assert(aEnd >= aStart); - assert(mLines.size() > (size_t)(aEnd - aStart)); +void TextEditor::RemoveLine(int aStart, int aEnd) { + assert(!mReadOnly); + assert(aEnd >= aStart); + assert(mLines.size() > (size_t)(aEnd - aStart)); - ErrorMarkers etmp; - for (auto& i : mErrorMarkers) - { - ErrorMarkers::value_type e(i.first >= aStart ? i.first - 1 : i.first, i.second); - if (e.first >= aStart && e.first <= aEnd) - continue; - etmp.insert(e); - } - mErrorMarkers = std::move(etmp); + ErrorMarkers etmp; + for (auto &i : mErrorMarkers) { + ErrorMarkers::value_type e(i.first >= aStart ? i.first - 1 : i.first, i.second); + if (e.first >= aStart && e.first <= aEnd) + continue; + etmp.insert(e); + } + mErrorMarkers = std::move(etmp); - Breakpoints btmp; - for (auto i : mBreakpoints) - { - if (i >= aStart && i <= aEnd) - continue; - btmp.insert(i >= aStart ? i - 1 : i); - } - mBreakpoints = std::move(btmp); + Breakpoints btmp; + for (auto i : mBreakpoints) { + if (i >= aStart && i <= aEnd) + continue; + btmp.insert(i >= aStart ? i - 1 : i); + } + mBreakpoints = std::move(btmp); - mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd); - assert(!mLines.empty()); + mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd); + assert(!mLines.empty()); - mTextChanged = true; + mTextChanged = true; } -void TextEditor::RemoveLine(int aIndex) -{ - assert(!mReadOnly); - assert(mLines.size() > 1); +void TextEditor::RemoveLine(int aIndex) { + assert(!mReadOnly); + assert(mLines.size() > 1); - ErrorMarkers etmp; - for (auto& i : mErrorMarkers) - { - ErrorMarkers::value_type e(i.first > aIndex ? i.first - 1 : i.first, i.second); - if (e.first - 1 == aIndex) - continue; - etmp.insert(e); - } - mErrorMarkers = std::move(etmp); + ErrorMarkers etmp; + for (auto &i : mErrorMarkers) { + ErrorMarkers::value_type e(i.first > aIndex ? i.first - 1 : i.first, i.second); + if (e.first - 1 == aIndex) + continue; + etmp.insert(e); + } + mErrorMarkers = std::move(etmp); - Breakpoints btmp; - for (auto i : mBreakpoints) - { - if (i == aIndex) - continue; - btmp.insert(i >= aIndex ? i - 1 : i); - } - mBreakpoints = std::move(btmp); + Breakpoints btmp; + for (auto i : mBreakpoints) { + if (i == aIndex) + continue; + btmp.insert(i >= aIndex ? i - 1 : i); + } + mBreakpoints = std::move(btmp); - mLines.erase(mLines.begin() + aIndex); - assert(!mLines.empty()); + mLines.erase(mLines.begin() + aIndex); + assert(!mLines.empty()); - mTextChanged = true; + mTextChanged = true; } -TextEditor::Line& TextEditor::InsertLine(int aIndex) -{ - assert(!mReadOnly); +TextEditor::Line &TextEditor::InsertLine(int aIndex) { + assert(!mReadOnly); - auto& result = *mLines.insert(mLines.begin() + aIndex, Line()); + auto &result = *mLines.insert(mLines.begin() + aIndex, Line()); - ErrorMarkers etmp; - for (auto& i : mErrorMarkers) - etmp.insert(ErrorMarkers::value_type(i.first >= aIndex ? i.first + 1 : i.first, i.second)); - mErrorMarkers = std::move(etmp); + ErrorMarkers etmp; + for (auto &i : mErrorMarkers) + etmp.insert(ErrorMarkers::value_type(i.first >= aIndex ? i.first + 1 : i.first, i.second)); + mErrorMarkers = std::move(etmp); - Breakpoints btmp; - for (auto i : mBreakpoints) - btmp.insert(i >= aIndex ? i + 1 : i); - mBreakpoints = std::move(btmp); + Breakpoints btmp; + for (auto i : mBreakpoints) + btmp.insert(i >= aIndex ? i + 1 : i); + mBreakpoints = std::move(btmp); - return result; + return result; } -std::string TextEditor::GetWordUnderCursor() const -{ - auto c = GetCursorPosition(); - return GetWordAt(c); +std::string TextEditor::GetWordUnderCursor() const { + auto c = GetCursorPosition(); + return GetWordAt(c); } -std::string TextEditor::GetWordAt(const Coordinates & aCoords) const -{ - auto start = FindWordStart(aCoords); - auto end = FindWordEnd(aCoords); +std::string TextEditor::GetWordAt(const Coordinates &aCoords) const { + auto start = FindWordStart(aCoords); + auto end = FindWordEnd(aCoords); - std::string r; + std::string r; - auto istart = GetCharacterIndex(start); - auto iend = GetCharacterIndex(end); + auto istart = GetCharacterIndex(start); + auto iend = GetCharacterIndex(end); - for (auto it = istart; it < iend; ++it) - r.push_back(mLines[aCoords.mLine][it].mChar); + for (auto it = istart; it < iend; ++it) + r.push_back(mLines[aCoords.mLine][it].mChar); - return r; + return r; } -ImU32 TextEditor::GetGlyphColor(const Glyph & aGlyph) const -{ - if (!mColorizerEnabled) - return mPalette[(int)PaletteIndex::Default]; - if (aGlyph.mComment) - return mPalette[(int)PaletteIndex::Comment]; - if (aGlyph.mMultiLineComment) - return mPalette[(int)PaletteIndex::MultiLineComment]; - auto const color = mPalette[(int)aGlyph.mColorIndex]; - if (aGlyph.mPreprocessor) - { - const auto ppcolor = mPalette[(int)PaletteIndex::Preprocessor]; - const int c0 = ((ppcolor & 0xff) + (color & 0xff)) / 2; - const int c1 = (((ppcolor >> 8) & 0xff) + ((color >> 8) & 0xff)) / 2; - const int c2 = (((ppcolor >> 16) & 0xff) + ((color >> 16) & 0xff)) / 2; - const int c3 = (((ppcolor >> 24) & 0xff) + ((color >> 24) & 0xff)) / 2; - return ImU32(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24)); - } - return color; +ImU32 TextEditor::GetGlyphColor(const Glyph &aGlyph) const { + if (!mColorizerEnabled) + return mPalette[(int)PaletteIndex::Default]; + if (aGlyph.mComment) + return mPalette[(int)PaletteIndex::Comment]; + if (aGlyph.mMultiLineComment) + return mPalette[(int)PaletteIndex::MultiLineComment]; + auto const color = mPalette[(int)aGlyph.mColorIndex]; + if (aGlyph.mPreprocessor) { + const auto ppcolor = mPalette[(int)PaletteIndex::Preprocessor]; + const int c0 = ((ppcolor & 0xff) + (color & 0xff)) / 2; + const int c1 = (((ppcolor >> 8) & 0xff) + ((color >> 8) & 0xff)) / 2; + const int c2 = (((ppcolor >> 16) & 0xff) + ((color >> 16) & 0xff)) / 2; + const int c3 = (((ppcolor >> 24) & 0xff) + ((color >> 24) & 0xff)) / 2; + return ImU32(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24)); + } + return color; } -void TextEditor::HandleKeyboardInputs() -{ - ImGuiIO& io = ImGui::GetIO(); - auto shift = io.KeyShift; - auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; - auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; +void TextEditor::HandleKeyboardInputs() { + ImGuiIO &io = ImGui::GetIO(); + auto shift = io.KeyShift; + auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; + auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; - if (ImGui::IsWindowFocused()) - { - if (ImGui::IsWindowHovered()) - ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); - //ImGui::CaptureKeyboardFromApp(true); + if (ImGui::IsWindowFocused()) { + if (ImGui::IsWindowHovered()) + ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); + // ImGui::CaptureKeyboardFromApp(true); - io.WantCaptureKeyboard = true; - io.WantTextInput = true; + io.WantCaptureKeyboard = true; + io.WantTextInput = true; - if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Z))) - Undo(); - else if (!IsReadOnly() && !ctrl && !shift && alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) - Undo(); - else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y))) - Redo(); - else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow))) - MoveUp(1, shift); - else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow))) - MoveDown(1, shift); - else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow))) - MoveLeft(1, shift, ctrl); - else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow))) - MoveRight(1, shift, ctrl); - else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp))) - MoveUp(GetPageSize() - 4, shift); - else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown))) - MoveDown(GetPageSize() - 4, shift); - else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) - MoveTop(shift); - else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) - MoveBottom(shift); - else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) - MoveHome(shift); - else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) - MoveEnd(shift); - else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) - Delete(); - else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) - Backspace(); - else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) - mOverwrite ^= true; - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) - Copy(); - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) - Copy(); - else if (!IsReadOnly() && !ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) - Paste(); - else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_V))) - Paste(); - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_X))) - Cut(); - else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) - Cut(); - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_A))) - SelectAll(); - else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter))) - EnterCharacter('\n', false); - else if (!IsReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Tab))) - EnterCharacter('\t', shift); + if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Z))) + Undo(); + else if (!IsReadOnly() && !ctrl && !shift && alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) + Undo(); + else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y))) + Redo(); + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow))) + MoveUp(1, shift); + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow))) + MoveDown(1, shift); + else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow))) + MoveLeft(1, shift, ctrl); + else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow))) + MoveRight(1, shift, ctrl); + else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp))) + MoveUp(GetPageSize() - 4, shift); + else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown))) + MoveDown(GetPageSize() - 4, shift); + else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) + MoveTop(shift); + else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) + MoveBottom(shift); + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) + MoveHome(shift); + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) + MoveEnd(shift); + else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) + Delete(); + else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) + Backspace(); + else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) + mOverwrite ^= true; + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) + Copy(); + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) + Copy(); + else if (!IsReadOnly() && !ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) + Paste(); + else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_V))) + Paste(); + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_X))) + Cut(); + else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) + Cut(); + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_A))) + SelectAll(); + else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter))) + EnterCharacter('\n', false); + else if (!IsReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Tab))) + EnterCharacter('\t', shift); - if (!IsReadOnly() && !io.InputQueueCharacters.empty()) - { - for (int i = 0; i < io.InputQueueCharacters.Size; i++) - { - auto c = io.InputQueueCharacters[i]; - if (c != 0 && (c == '\n' || c >= 32)) { - constexpr std::array, 5> doubleChars = {{ { '(', ')' }, { '[', ']' }, { '{', '}' }, { '"', '"' }, {'\'', '\'' } }}; + if (!IsReadOnly() && !io.InputQueueCharacters.empty()) { + for (int i = 0; i < io.InputQueueCharacters.Size; i++) { + auto c = io.InputQueueCharacters[i]; + if (c != 0 && (c == '\n' || c >= 32)) { + EnterCharacter(c, shift); + } + } + io.InputQueueCharacters.resize(0); + } + } +} - bool handled = false; - for (auto [doubleCharOpen, doubleCharClose] : doubleChars) { - if (c == doubleCharOpen) { - EnterCharacter(doubleCharOpen, shift); - auto cursorPos = GetCursorPosition(); - EnterCharacter(doubleCharClose, shift); - SetCursorPosition(cursorPos); +void TextEditor::HandleMouseInputs() { + ImGuiIO &io = ImGui::GetIO(); + auto shift = io.KeyShift; + auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; + auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; - handled = true; + if (ImGui::IsWindowHovered()) { + if (!shift && !alt) { + auto click = ImGui::IsMouseClicked(0); + auto doubleClick = ImGui::IsMouseDoubleClicked(0); + auto t = ImGui::GetTime(); + auto tripleClick = click && !doubleClick && (mLastClick != -1.0f && (t - mLastClick) < io.MouseDoubleClickTime); - break; + /* + Left mouse button triple click + */ + + if (tripleClick) { + if (!ctrl) { + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + mSelectionMode = SelectionMode::Line; + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + } + + mLastClick = -1.0f; + } + + /* + Left mouse button double click + */ + + else if (doubleClick) { + if (!ctrl) { + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + if (mSelectionMode == SelectionMode::Line) + mSelectionMode = SelectionMode::Normal; + else + mSelectionMode = SelectionMode::Word; + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + } + + mLastClick = (float)ImGui::GetTime(); + } + + /* + Left mouse button click + */ + else if (click) { + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + if (ctrl) + mSelectionMode = SelectionMode::Word; + else + mSelectionMode = SelectionMode::Normal; + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + + mLastClick = (float)ImGui::GetTime(); + } + // Mouse left button dragging (=> update selection) + else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) { + io.WantCaptureMouse = true; + mState.mCursorPosition = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + } + } + } +} + +void TextEditor::Render() { + /* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/ + const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; + mCharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing); + + /* Update palette with the current alpha from style */ + for (int i = 0; i < (int)PaletteIndex::Max; ++i) { + auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]); + color.w *= ImGui::GetStyle().Alpha; + mPalette[i] = ImGui::ColorConvertFloat4ToU32(color); + } + + assert(mLineBuffer.empty()); + + auto contentSize = ImGui::GetWindowContentRegionMax(); + auto drawList = ImGui::GetWindowDrawList(); + float longest(mTextStart); + + if (mScrollToTop) { + mScrollToTop = false; + ImGui::SetScrollY(0.f); + } + + ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); + auto scrollX = ImGui::GetScrollX(); + auto scrollY = ImGui::GetScrollY(); + + auto lineNo = (int)floor(scrollY / mCharAdvance.y); + auto globalLineMax = (int)mLines.size(); + auto lineMax = std::max(0, std::min((int)mLines.size() - 1, lineNo + (int)floor((scrollY + contentSize.y) / mCharAdvance.y))); + + // Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width + char buf[16]; + snprintf(buf, 16, " %d ", globalLineMax); + mTextStart = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x + mLeftMargin; + + if (!mLines.empty()) { + float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x; + + while (lineNo <= lineMax) { + ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * mCharAdvance.y); + ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + mTextStart, lineStartScreenPos.y); + + auto &line = mLines[lineNo]; + longest = std::max(mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest); + auto columnNo = 0; + Coordinates lineStartCoord(lineNo, 0); + Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo)); + + // Draw selection for the current line + float sstart = -1.0f; + float ssend = -1.0f; + + assert(mState.mSelectionStart <= mState.mSelectionEnd); + if (mState.mSelectionStart <= lineEndCoord) + sstart = mState.mSelectionStart > lineStartCoord ? TextDistanceToLineStart(mState.mSelectionStart) : 0.0f; + if (mState.mSelectionEnd > lineStartCoord) + ssend = TextDistanceToLineStart(mState.mSelectionEnd < lineEndCoord ? mState.mSelectionEnd : lineEndCoord); + + if (mState.mSelectionEnd.mLine > lineNo) + ssend += mCharAdvance.x; + + if (sstart != -1 && ssend != -1 && sstart < ssend) { + ImVec2 vstart(lineStartScreenPos.x + mTextStart + sstart, lineStartScreenPos.y); + ImVec2 vend(lineStartScreenPos.x + mTextStart + ssend, lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]); + } + + // Draw breakpoints + auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y); + + if (mBreakpoints.count(lineNo + 1) != 0) { + auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]); + } + + // Draw error markers + auto errorIt = mErrorMarkers.find(lineNo + 1); + if (errorIt != mErrorMarkers.end()) { + auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::ErrorMarker]); + + if (ImGui::IsMouseHoveringRect(lineStartScreenPos, end)) { + ImGui::BeginTooltip(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f)); + ImGui::Text("Error at line %d:", errorIt->first); + ImGui::PopStyleColor(); + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.2f, 1.0f)); + ImGui::Text("%s", errorIt->second.c_str()); + ImGui::PopStyleColor(); + ImGui::EndTooltip(); + } + } + + // Draw line number (right aligned) + snprintf(buf, 16, "%d ", lineNo + 1); + + auto lineNoWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x; + drawList->AddText(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y), mPalette[(int)PaletteIndex::LineNumber], buf); + + if (mState.mCursorPosition.mLine == lineNo) { + auto focused = ImGui::IsWindowFocused(); + + // Highlight the current line (where the cursor is) + if (!HasSelection()) { + auto end = ImVec2(start.x + contentSize.x + scrollX, start.y + mCharAdvance.y); + drawList->AddRectFilled(start, end, mPalette[(int)(focused ? PaletteIndex::CurrentLineFill : PaletteIndex::CurrentLineFillInactive)]); + drawList->AddRect(start, end, mPalette[(int)PaletteIndex::CurrentLineEdge], 1.0f); + } + + // Render the cursor + if (focused) { + auto timeEnd = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + auto elapsed = timeEnd - mStartTime; + if (elapsed > 400) { + float width = 1.0f; + auto cindex = GetCharacterIndex(mState.mCursorPosition); + float cx = TextDistanceToLineStart(mState.mCursorPosition); + + if (mOverwrite && cindex < (int)line.size()) { + auto c = line[cindex].mChar; + if (c == '\t') { + auto x = (1.0f + std::floor((1.0f + cx) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); + width = x - cx; + } else { + char buf2[2]; + buf2[0] = line[cindex].mChar; + buf2[1] = '\0'; + width = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf2).x; + } + } + ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y); + ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]); + if (elapsed > 800) + mStartTime = timeEnd; + } + } + } + + // Render colorized text + auto prevColor = line.empty() ? mPalette[(int)PaletteIndex::Default] : GetGlyphColor(line[0]); + ImVec2 bufferOffset; + + for (int i = 0; i < line.size();) { + auto &glyph = line[i]; + auto color = GetGlyphColor(glyph); + + if ((color != prevColor || glyph.mChar == '\t' || glyph.mChar == ' ') && !mLineBuffer.empty()) { + const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y); + drawList->AddText(newOffset, prevColor, mLineBuffer.c_str()); + auto textSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, mLineBuffer.c_str(), nullptr, nullptr); + bufferOffset.x += textSize.x; + mLineBuffer.clear(); + } + prevColor = color; + + if (glyph.mChar == '\t') { + auto oldX = bufferOffset.x; + bufferOffset.x = (1.0f + std::floor((1.0f + bufferOffset.x) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); + ++i; + + if (mShowWhitespaces) { + const auto s = ImGui::GetFontSize(); + const auto x1 = textScreenPos.x + oldX + 1.0f; + const auto x2 = textScreenPos.x + bufferOffset.x - 1.0f; + const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f; + const ImVec2 p1(x1, y); + const ImVec2 p2(x2, y); + const ImVec2 p3(x2 - s * 0.2f, y - s * 0.2f); + const ImVec2 p4(x2 - s * 0.2f, y + s * 0.2f); + drawList->AddLine(p1, p2, 0x90909090); + drawList->AddLine(p2, p3, 0x90909090); + drawList->AddLine(p2, p4, 0x90909090); + } + } else if (glyph.mChar == ' ') { + if (mShowWhitespaces) { + const auto s = ImGui::GetFontSize(); + const auto x = textScreenPos.x + bufferOffset.x + spaceSize * 0.5f; + const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f; + drawList->AddCircleFilled(ImVec2(x, y), 1.5f, 0x80808080, 4); + } + bufferOffset.x += spaceSize; + i++; + } else { + auto l = UTF8CharLength(glyph.mChar); + while (l-- > 0) + mLineBuffer.push_back(line[i++].mChar); + } + ++columnNo; + } + + if (!mLineBuffer.empty()) { + const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y); + drawList->AddText(newOffset, prevColor, mLineBuffer.c_str()); + mLineBuffer.clear(); + } + + ++lineNo; + } + + // Draw a tooltip on known identifiers/preprocessor symbols + if (ImGui::IsMousePosValid()) { + auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos())); + if (!id.empty()) { + auto it = mLanguageDefinition.mIdentifiers.find(id); + if (it != mLanguageDefinition.mIdentifiers.end() && !it->second.mDeclaration.empty()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(it->second.mDeclaration.c_str()); + ImGui::EndTooltip(); + } else { + auto pi = mLanguageDefinition.mPreprocIdentifiers.find(id); + if (pi != mLanguageDefinition.mPreprocIdentifiers.end() && !pi->second.mDeclaration.empty()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(pi->second.mDeclaration.c_str()); + ImGui::EndTooltip(); + } + } + } + } + } + + + ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y)); + + if (mScrollToCursor) { + EnsureCursorVisible(); + ImGui::SetWindowFocus(); + mScrollToCursor = false; + } +} + +void TextEditor::Render(const char *aTitle, const ImVec2 &aSize, bool aBorder) { + mWithinRender = true; + mTextChanged = false; + mCursorPositionChanged = false; + + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background])); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + if (!mIgnoreImGuiChild) + ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoMove); + + if (mHandleKeyboardInputs) { + HandleKeyboardInputs(); + ImGui::PushAllowKeyboardFocus(true); + } + + if (mHandleMouseInputs) + HandleMouseInputs(); + + ColorizeInternal(); + Render(); + + if (mHandleKeyboardInputs) + ImGui::PopAllowKeyboardFocus(); + + if (!mIgnoreImGuiChild) + ImGui::EndChild(); + + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + + mWithinRender = false; +} + +void TextEditor::SetText(const std::string &aText) { + mLines.clear(); + mLines.emplace_back(Line()); + for (auto chr : aText) { + if (chr == '\r') { + // ignore the carriage return character + } else if (chr == '\n') + mLines.emplace_back(Line()); + else { + mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default)); + } + } + + mTextChanged = true; + mScrollToTop = true; + + mUndoBuffer.clear(); + mUndoIndex = 0; + + Colorize(); +} + +void TextEditor::SetTextLines(const std::vector &aLines) { + mLines.clear(); + + if (aLines.empty()) { + mLines.emplace_back(Line()); + } else { + mLines.resize(aLines.size()); + + for (size_t i = 0; i < aLines.size(); ++i) { + const std::string &aLine = aLines[i]; + + mLines[i].reserve(aLine.size()); + for (size_t j = 0; j < aLine.size(); ++j) + mLines[i].emplace_back(Glyph(aLine[j], PaletteIndex::Default)); + } + } + + mTextChanged = true; + mScrollToTop = true; + + mUndoBuffer.clear(); + mUndoIndex = 0; + + Colorize(); +} + +void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) { + assert(!mReadOnly); + + UndoRecord u; + + u.mBefore = mState; + + if (HasSelection()) { + if (aChar == '\t' && mState.mSelectionStart.mLine != mState.mSelectionEnd.mLine) { + + auto start = mState.mSelectionStart; + auto end = mState.mSelectionEnd; + auto originalEnd = end; + + if (start > end) + std::swap(start, end); + start.mColumn = 0; + // end.mColumn = end.mLine < mLines.size() ? mLines[end.mLine].size() : 0; + if (end.mColumn == 0 && end.mLine > 0) + --end.mLine; + if (end.mLine >= (int)mLines.size()) + end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1; + end.mColumn = GetLineMaxColumn(end.mLine); + + // if (end.mColumn >= GetLineMaxColumn(end.mLine)) + // end.mColumn = GetLineMaxColumn(end.mLine) - 1; + + u.mRemovedStart = start; + u.mRemovedEnd = end; + u.mRemoved = GetText(start, end); + + bool modified = false; + + for (int i = start.mLine; i <= end.mLine; i++) { + auto &line = mLines[i]; + if (aShift) { + if (!line.empty()) { + if (line.front().mChar == '\t') { + line.erase(line.begin()); + modified = true; + } else { + for (int j = 0; j < mTabSize && !line.empty() && line.front().mChar == ' '; j++) { + line.erase(line.begin()); + modified = true; + } } } - - if (!handled) - EnterCharacter(c, shift); + } else { + line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background)); + modified = true; } - } - io.InputQueueCharacters.resize(0); - } - } + } + + if (modified) { + start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0)); + Coordinates rangeEnd; + if (originalEnd.mColumn != 0) { + end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine)); + rangeEnd = end; + u.mAdded = GetText(start, end); + } else { + end = Coordinates(originalEnd.mLine, 0); + rangeEnd = Coordinates(end.mLine - 1, GetLineMaxColumn(end.mLine - 1)); + u.mAdded = GetText(start, rangeEnd); + } + + u.mAddedStart = start; + u.mAddedEnd = rangeEnd; + u.mAfter = mState; + + mState.mSelectionStart = start; + mState.mSelectionEnd = end; + AddUndo(u); + + mTextChanged = true; + + EnsureCursorVisible(); + } + + return; + } // c == '\t' + else { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + DeleteSelection(); + } + } // HasSelection + + auto coord = GetActualCursorCoordinates(); + u.mAddedStart = coord; + + assert(!mLines.empty()); + + if (aChar == '\n') { + InsertLine(coord.mLine + 1); + auto &line = mLines[coord.mLine]; + auto &newLine = mLines[coord.mLine + 1]; + + if (mLanguageDefinition.mAutoIndentation) + for (size_t it = 0; it < line.size() && isascii(line[it].mChar) && isblank(line[it].mChar); ++it) + newLine.push_back(line[it]); + + const size_t whitespaceSize = newLine.size(); + auto cindex = GetCharacterIndex(coord); + newLine.insert(newLine.end(), line.begin() + cindex, line.end()); + line.erase(line.begin() + cindex, line.begin() + line.size()); + SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize))); + u.mAdded = (char)aChar; + } else { + char buf[7]; + int e = ImTextCharToUtf8(buf, 7, aChar); + if (e > 0) { + buf[e] = '\0'; + auto &line = mLines[coord.mLine]; + auto cindex = GetCharacterIndex(coord); + + if (mOverwrite && cindex < (int)line.size()) { + auto d = UTF8CharLength(line[cindex].mChar); + + u.mRemovedStart = mState.mCursorPosition; + u.mRemovedEnd = Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + d)); + + while (d-- > 0 && cindex < (int)line.size()) { + u.mRemoved += line[cindex].mChar; + line.erase(line.begin() + cindex); + } + } + + for (auto p = buf; *p != '\0'; p++, ++cindex) + line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default)); + u.mAdded = buf; + + SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex))); + } else + return; + } + + mTextChanged = true; + + u.mAddedEnd = GetActualCursorCoordinates(); + u.mAfter = mState; + + AddUndo(u); + + Colorize(coord.mLine - 1, 3); + EnsureCursorVisible(); } -void TextEditor::HandleMouseInputs() -{ - ImGuiIO& io = ImGui::GetIO(); - auto shift = io.KeyShift; - auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; - auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; - - if (ImGui::IsWindowHovered()) - { - if (!shift && !alt) - { - auto click = ImGui::IsMouseClicked(0); - auto doubleClick = ImGui::IsMouseDoubleClicked(0); - auto t = ImGui::GetTime(); - auto tripleClick = click && !doubleClick && (mLastClick != -1.0f && (t - mLastClick) < io.MouseDoubleClickTime); - - /* - Left mouse button triple click - */ - - if (tripleClick) - { - if (!ctrl) - { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); - mSelectionMode = SelectionMode::Line; - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); - } - - mLastClick = -1.0f; - } - - /* - Left mouse button double click - */ - - else if (doubleClick) - { - if (!ctrl) - { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); - if (mSelectionMode == SelectionMode::Line) - mSelectionMode = SelectionMode::Normal; - else - mSelectionMode = SelectionMode::Word; - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); - } - - mLastClick = (float)ImGui::GetTime(); - } - - /* - Left mouse button click - */ - else if (click) - { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); - if (ctrl) - mSelectionMode = SelectionMode::Word; - else - mSelectionMode = SelectionMode::Normal; - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); - - mLastClick = (float)ImGui::GetTime(); - } - // Mouse left button dragging (=> update selection) - else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) - { - io.WantCaptureMouse = true; - mState.mCursorPosition = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); - } - } - } +void TextEditor::SetReadOnly(bool aValue) { + mReadOnly = aValue; } -void TextEditor::Render() -{ - /* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/ - const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; - mCharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing); - - /* Update palette with the current alpha from style */ - for (int i = 0; i < (int)PaletteIndex::Max; ++i) - { - auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]); - color.w *= ImGui::GetStyle().Alpha; - mPalette[i] = ImGui::ColorConvertFloat4ToU32(color); - } - - assert(mLineBuffer.empty()); - - auto contentSize = ImGui::GetWindowContentRegionMax(); - auto drawList = ImGui::GetWindowDrawList(); - float longest(mTextStart); - - if (mScrollToTop) - { - mScrollToTop = false; - ImGui::SetScrollY(0.f); - } - - ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); - auto scrollX = ImGui::GetScrollX(); - auto scrollY = ImGui::GetScrollY(); - - auto lineNo = (int)floor(scrollY / mCharAdvance.y); - auto globalLineMax = (int)mLines.size(); - auto lineMax = std::max(0, std::min((int)mLines.size() - 1, lineNo + (int)floor((scrollY + contentSize.y) / mCharAdvance.y))); - - // Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width - char buf[16]; - snprintf(buf, 16, " %d ", globalLineMax); - mTextStart = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x + mLeftMargin; - - if (!mLines.empty()) - { - float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x; - - while (lineNo <= lineMax) - { - ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * mCharAdvance.y); - ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + mTextStart, lineStartScreenPos.y); - - auto& line = mLines[lineNo]; - longest = std::max(mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest); - auto columnNo = 0; - Coordinates lineStartCoord(lineNo, 0); - Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo)); - - // Draw selection for the current line - float sstart = -1.0f; - float ssend = -1.0f; - - assert(mState.mSelectionStart <= mState.mSelectionEnd); - if (mState.mSelectionStart <= lineEndCoord) - sstart = mState.mSelectionStart > lineStartCoord ? TextDistanceToLineStart(mState.mSelectionStart) : 0.0f; - if (mState.mSelectionEnd > lineStartCoord) - ssend = TextDistanceToLineStart(mState.mSelectionEnd < lineEndCoord ? mState.mSelectionEnd : lineEndCoord); - - if (mState.mSelectionEnd.mLine > lineNo) - ssend += mCharAdvance.x; - - if (sstart != -1 && ssend != -1 && sstart < ssend) - { - ImVec2 vstart(lineStartScreenPos.x + mTextStart + sstart, lineStartScreenPos.y); - ImVec2 vend(lineStartScreenPos.x + mTextStart + ssend, lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]); - } - - // Draw breakpoints - auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y); - - if (mBreakpoints.count(lineNo + 1) != 0) - { - auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]); - } - - // Draw error markers - auto errorIt = mErrorMarkers.find(lineNo + 1); - if (errorIt != mErrorMarkers.end()) - { - auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::ErrorMarker]); - - if (ImGui::IsMouseHoveringRect(lineStartScreenPos, end)) - { - ImGui::BeginTooltip(); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f)); - ImGui::Text("Error at line %d:", errorIt->first); - ImGui::PopStyleColor(); - ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.2f, 1.0f)); - ImGui::Text("%s", errorIt->second.c_str()); - ImGui::PopStyleColor(); - ImGui::EndTooltip(); - } - } - - // Draw line number (right aligned) - snprintf(buf, 16, "%d ", lineNo + 1); - - auto lineNoWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x; - drawList->AddText(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y), mPalette[(int)PaletteIndex::LineNumber], buf); - - if (mState.mCursorPosition.mLine == lineNo) - { - auto focused = ImGui::IsWindowFocused(); - - // Highlight the current line (where the cursor is) - if (!HasSelection()) - { - auto end = ImVec2(start.x + contentSize.x + scrollX, start.y + mCharAdvance.y); - drawList->AddRectFilled(start, end, mPalette[(int)(focused ? PaletteIndex::CurrentLineFill : PaletteIndex::CurrentLineFillInactive)]); - drawList->AddRect(start, end, mPalette[(int)PaletteIndex::CurrentLineEdge], 1.0f); - } - - // Render the cursor - if (focused) - { - auto timeEnd = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - auto elapsed = timeEnd - mStartTime; - if (elapsed > 400) - { - float width = 1.0f; - auto cindex = GetCharacterIndex(mState.mCursorPosition); - float cx = TextDistanceToLineStart(mState.mCursorPosition); - - if (mOverwrite && cindex < (int)line.size()) - { - auto c = line[cindex].mChar; - if (c == '\t') - { - auto x = (1.0f + std::floor((1.0f + cx) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); - width = x - cx; - } - else - { - char buf2[2]; - buf2[0] = line[cindex].mChar; - buf2[1] = '\0'; - width = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf2).x; - } - } - ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y); - ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]); - if (elapsed > 800) - mStartTime = timeEnd; - } - } - } - - // Render colorized text - auto prevColor = line.empty() ? mPalette[(int)PaletteIndex::Default] : GetGlyphColor(line[0]); - ImVec2 bufferOffset; - - for (int i = 0; i < line.size();) - { - auto& glyph = line[i]; - auto color = GetGlyphColor(glyph); - - if ((color != prevColor || glyph.mChar == '\t' || glyph.mChar == ' ') && !mLineBuffer.empty()) - { - const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y); - drawList->AddText(newOffset, prevColor, mLineBuffer.c_str()); - auto textSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, mLineBuffer.c_str(), nullptr, nullptr); - bufferOffset.x += textSize.x; - mLineBuffer.clear(); - } - prevColor = color; - - if (glyph.mChar == '\t') - { - auto oldX = bufferOffset.x; - bufferOffset.x = (1.0f + std::floor((1.0f + bufferOffset.x) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); - ++i; - - if (mShowWhitespaces) - { - const auto s = ImGui::GetFontSize(); - const auto x1 = textScreenPos.x + oldX + 1.0f; - const auto x2 = textScreenPos.x + bufferOffset.x - 1.0f; - const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f; - const ImVec2 p1(x1, y); - const ImVec2 p2(x2, y); - const ImVec2 p3(x2 - s * 0.2f, y - s * 0.2f); - const ImVec2 p4(x2 - s * 0.2f, y + s * 0.2f); - drawList->AddLine(p1, p2, 0x90909090); - drawList->AddLine(p2, p3, 0x90909090); - drawList->AddLine(p2, p4, 0x90909090); - } - } - else if (glyph.mChar == ' ') - { - if (mShowWhitespaces) - { - const auto s = ImGui::GetFontSize(); - const auto x = textScreenPos.x + bufferOffset.x + spaceSize * 0.5f; - const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f; - drawList->AddCircleFilled(ImVec2(x, y), 1.5f, 0x80808080, 4); - } - bufferOffset.x += spaceSize; - i++; - } - else - { - auto l = UTF8CharLength(glyph.mChar); - while (l-- > 0) - mLineBuffer.push_back(line[i++].mChar); - } - ++columnNo; - } - - if (!mLineBuffer.empty()) - { - const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y); - drawList->AddText(newOffset, prevColor, mLineBuffer.c_str()); - mLineBuffer.clear(); - } - - ++lineNo; - } - - // Draw a tooltip on known identifiers/preprocessor symbols - if (ImGui::IsMousePosValid()) - { - auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos())); - if (!id.empty()) - { - auto it = mLanguageDefinition.mIdentifiers.find(id); - if (it != mLanguageDefinition.mIdentifiers.end()) - { - ImGui::BeginTooltip(); - ImGui::TextUnformatted(it->second.mDeclaration.c_str()); - ImGui::EndTooltip(); - } - else - { - auto pi = mLanguageDefinition.mPreprocIdentifiers.find(id); - if (pi != mLanguageDefinition.mPreprocIdentifiers.end()) - { - ImGui::BeginTooltip(); - ImGui::TextUnformatted(pi->second.mDeclaration.c_str()); - ImGui::EndTooltip(); - } - } - } - } - } - - - ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y)); - - if (mScrollToCursor) - { - EnsureCursorVisible(); - ImGui::SetWindowFocus(); - mScrollToCursor = false; - } +void TextEditor::SetColorizerEnable(bool aValue) { + mColorizerEnabled = aValue; } -void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) -{ - mWithinRender = true; - mTextChanged = false; - mCursorPositionChanged = false; - - ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background])); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); - if (!mIgnoreImGuiChild) - ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoMove); - - if (mHandleKeyboardInputs) - { - HandleKeyboardInputs(); - ImGui::PushAllowKeyboardFocus(true); - } - - if (mHandleMouseInputs) - HandleMouseInputs(); - - ColorizeInternal(); - Render(); - - if (mHandleKeyboardInputs) - ImGui::PopAllowKeyboardFocus(); - - if (!mIgnoreImGuiChild) - ImGui::EndChild(); - - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - - mWithinRender = false; +void TextEditor::SetCursorPosition(const Coordinates &aPosition) { + if (mState.mCursorPosition != aPosition) { + mState.mCursorPosition = aPosition; + mCursorPositionChanged = true; + EnsureCursorVisible(); + } } -void TextEditor::SetText(const std::string & aText) -{ - mLines.clear(); - mLines.emplace_back(Line()); - for (auto chr : aText) - { - if (chr == '\r') - { - // ignore the carriage return character - } - else if (chr == '\n') - mLines.emplace_back(Line()); - else - { - mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default)); - } - } - - mTextChanged = true; - mScrollToTop = true; - - mUndoBuffer.clear(); - mUndoIndex = 0; - - Colorize(); +void TextEditor::SetSelectionStart(const Coordinates &aPosition) { + mState.mSelectionStart = SanitizeCoordinates(aPosition); + if (mState.mSelectionStart > mState.mSelectionEnd) + std::swap(mState.mSelectionStart, mState.mSelectionEnd); } -void TextEditor::SetTextLines(const std::vector & aLines) -{ - mLines.clear(); - - if (aLines.empty()) - { - mLines.emplace_back(Line()); - } - else - { - mLines.resize(aLines.size()); - - for (size_t i = 0; i < aLines.size(); ++i) - { - const std::string & aLine = aLines[i]; - - mLines[i].reserve(aLine.size()); - for (size_t j = 0; j < aLine.size(); ++j) - mLines[i].emplace_back(Glyph(aLine[j], PaletteIndex::Default)); - } - } - - mTextChanged = true; - mScrollToTop = true; - - mUndoBuffer.clear(); - mUndoIndex = 0; - - Colorize(); +void TextEditor::SetSelectionEnd(const Coordinates &aPosition) { + mState.mSelectionEnd = SanitizeCoordinates(aPosition); + if (mState.mSelectionStart > mState.mSelectionEnd) + std::swap(mState.mSelectionStart, mState.mSelectionEnd); } -void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) -{ - assert(!mReadOnly); +void TextEditor::SetSelection(const Coordinates &aStart, const Coordinates &aEnd, SelectionMode aMode) { + auto oldSelStart = mState.mSelectionStart; + auto oldSelEnd = mState.mSelectionEnd; - UndoRecord u; + mState.mSelectionStart = SanitizeCoordinates(aStart); + mState.mSelectionEnd = SanitizeCoordinates(aEnd); + if (mState.mSelectionStart > mState.mSelectionEnd) + std::swap(mState.mSelectionStart, mState.mSelectionEnd); - u.mBefore = mState; + switch (aMode) { + case TextEditor::SelectionMode::Normal: + break; + case TextEditor::SelectionMode::Word: + { + mState.mSelectionStart = FindWordStart(mState.mSelectionStart); + if (!IsOnWordBoundary(mState.mSelectionEnd)) + mState.mSelectionEnd = FindWordEnd(FindWordStart(mState.mSelectionEnd)); + break; + } + case TextEditor::SelectionMode::Line: + { + const auto lineNo = mState.mSelectionEnd.mLine; + const auto lineSize = (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0; + mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0); + mState.mSelectionEnd = Coordinates(lineNo, GetLineMaxColumn(lineNo)); + break; + } + default: + break; + } - if (HasSelection()) - { - if (aChar == '\t' && mState.mSelectionStart.mLine != mState.mSelectionEnd.mLine) - { - - auto start = mState.mSelectionStart; - auto end = mState.mSelectionEnd; - auto originalEnd = end; - - if (start > end) - std::swap(start, end); - start.mColumn = 0; - // end.mColumn = end.mLine < mLines.size() ? mLines[end.mLine].size() : 0; - if (end.mColumn == 0 && end.mLine > 0) - --end.mLine; - if (end.mLine >= (int)mLines.size()) - end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1; - end.mColumn = GetLineMaxColumn(end.mLine); - - //if (end.mColumn >= GetLineMaxColumn(end.mLine)) - // end.mColumn = GetLineMaxColumn(end.mLine) - 1; - - u.mRemovedStart = start; - u.mRemovedEnd = end; - u.mRemoved = GetText(start, end); - - bool modified = false; - - for (int i = start.mLine; i <= end.mLine; i++) - { - auto& line = mLines[i]; - if (aShift) - { - if (!line.empty()) - { - if (line.front().mChar == '\t') - { - line.erase(line.begin()); - modified = true; - } - else - { - for (int j = 0; j < mTabSize && !line.empty() && line.front().mChar == ' '; j++) - { - line.erase(line.begin()); - modified = true; - } - } - } - } - else - { - line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background)); - modified = true; - } - } - - if (modified) - { - start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0)); - Coordinates rangeEnd; - if (originalEnd.mColumn != 0) - { - end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine)); - rangeEnd = end; - u.mAdded = GetText(start, end); - } - else - { - end = Coordinates(originalEnd.mLine, 0); - rangeEnd = Coordinates(end.mLine - 1, GetLineMaxColumn(end.mLine - 1)); - u.mAdded = GetText(start, rangeEnd); - } - - u.mAddedStart = start; - u.mAddedEnd = rangeEnd; - u.mAfter = mState; - - mState.mSelectionStart = start; - mState.mSelectionEnd = end; - AddUndo(u); - - mTextChanged = true; - - EnsureCursorVisible(); - } - - return; - } // c == '\t' - else - { - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - DeleteSelection(); - } - } // HasSelection - - auto coord = GetActualCursorCoordinates(); - u.mAddedStart = coord; - - assert(!mLines.empty()); - - if (aChar == '\n') - { - InsertLine(coord.mLine + 1); - auto& line = mLines[coord.mLine]; - auto& newLine = mLines[coord.mLine + 1]; - - if (mLanguageDefinition.mAutoIndentation) - for (size_t it = 0; it < line.size() && isascii(line[it].mChar) && isblank(line[it].mChar); ++it) - newLine.push_back(line[it]); - - const size_t whitespaceSize = newLine.size(); - auto cindex = GetCharacterIndex(coord); - newLine.insert(newLine.end(), line.begin() + cindex, line.end()); - line.erase(line.begin() + cindex, line.begin() + line.size()); - SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize))); - u.mAdded = (char)aChar; - } - else - { - char buf[7]; - int e = ImTextCharToUtf8(buf, 7, aChar); - if (e > 0) - { - buf[e] = '\0'; - auto& line = mLines[coord.mLine]; - auto cindex = GetCharacterIndex(coord); - - if (mOverwrite && cindex < (int)line.size()) - { - auto d = UTF8CharLength(line[cindex].mChar); - - u.mRemovedStart = mState.mCursorPosition; - u.mRemovedEnd = Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + d)); - - while (d-- > 0 && cindex < (int)line.size()) - { - u.mRemoved += line[cindex].mChar; - line.erase(line.begin() + cindex); - } - } - - for (auto p = buf; *p != '\0'; p++, ++cindex) - line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default)); - u.mAdded = buf; - - SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex))); - } - else - return; - } - - mTextChanged = true; - - u.mAddedEnd = GetActualCursorCoordinates(); - u.mAfter = mState; - - AddUndo(u); - - Colorize(coord.mLine - 1, 3); - EnsureCursorVisible(); + if (mState.mSelectionStart != oldSelStart || + mState.mSelectionEnd != oldSelEnd) + mCursorPositionChanged = true; } -void TextEditor::SetReadOnly(bool aValue) -{ - mReadOnly = aValue; +void TextEditor::SetTabSize(int aValue) { + mTabSize = std::max(0, std::min(32, aValue)); } -void TextEditor::SetColorizerEnable(bool aValue) -{ - mColorizerEnabled = aValue; +void TextEditor::InsertText(const std::string &aValue) { + InsertText(aValue.c_str()); } -void TextEditor::SetCursorPosition(const Coordinates & aPosition) -{ - if (mState.mCursorPosition != aPosition) - { - mState.mCursorPosition = aPosition; - mCursorPositionChanged = true; - EnsureCursorVisible(); - } +void TextEditor::InsertText(const char *aValue) { + if (aValue == nullptr) + return; + + auto pos = GetActualCursorCoordinates(); + auto start = std::min(pos, mState.mSelectionStart); + int totalLines = pos.mLine - start.mLine; + + totalLines += InsertTextAt(pos, aValue); + + SetSelection(pos, pos); + SetCursorPosition(pos); + Colorize(start.mLine - 1, totalLines + 2); } -void TextEditor::SetSelectionStart(const Coordinates & aPosition) -{ - mState.mSelectionStart = SanitizeCoordinates(aPosition); - if (mState.mSelectionStart > mState.mSelectionEnd) - std::swap(mState.mSelectionStart, mState.mSelectionEnd); +void TextEditor::DeleteSelection() { + assert(mState.mSelectionEnd >= mState.mSelectionStart); + + if (mState.mSelectionEnd == mState.mSelectionStart) + return; + + DeleteRange(mState.mSelectionStart, mState.mSelectionEnd); + + SetSelection(mState.mSelectionStart, mState.mSelectionStart); + SetCursorPosition(mState.mSelectionStart); + Colorize(mState.mSelectionStart.mLine, 1); } -void TextEditor::SetSelectionEnd(const Coordinates & aPosition) -{ - mState.mSelectionEnd = SanitizeCoordinates(aPosition); - if (mState.mSelectionStart > mState.mSelectionEnd) - std::swap(mState.mSelectionStart, mState.mSelectionEnd); +void TextEditor::MoveUp(int aAmount, bool aSelect) { + auto oldPos = mState.mCursorPosition; + mState.mCursorPosition.mLine = std::max(0, mState.mCursorPosition.mLine - aAmount); + if (oldPos != mState.mCursorPosition) { + if (aSelect) { + if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else { + mInteractiveStart = mState.mCursorPosition; + mInteractiveEnd = oldPos; + } + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + + EnsureCursorVisible(); + } } -void TextEditor::SetSelection(const Coordinates & aStart, const Coordinates & aEnd, SelectionMode aMode) -{ - auto oldSelStart = mState.mSelectionStart; - auto oldSelEnd = mState.mSelectionEnd; +void TextEditor::MoveDown(int aAmount, bool aSelect) { + assert(mState.mCursorPosition.mColumn >= 0); + auto oldPos = mState.mCursorPosition; + mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + aAmount)); - mState.mSelectionStart = SanitizeCoordinates(aStart); - mState.mSelectionEnd = SanitizeCoordinates(aEnd); - if (mState.mSelectionStart > mState.mSelectionEnd) - std::swap(mState.mSelectionStart, mState.mSelectionEnd); + if (mState.mCursorPosition != oldPos) { + if (aSelect) { + if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else { + mInteractiveStart = oldPos; + mInteractiveEnd = mState.mCursorPosition; + } + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); - switch (aMode) - { - case TextEditor::SelectionMode::Normal: - break; - case TextEditor::SelectionMode::Word: - { - mState.mSelectionStart = FindWordStart(mState.mSelectionStart); - if (!IsOnWordBoundary(mState.mSelectionEnd)) - mState.mSelectionEnd = FindWordEnd(FindWordStart(mState.mSelectionEnd)); - break; - } - case TextEditor::SelectionMode::Line: - { - const auto lineNo = mState.mSelectionEnd.mLine; - const auto lineSize = (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0; - mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0); - mState.mSelectionEnd = Coordinates(lineNo, GetLineMaxColumn(lineNo)); - break; - } - default: - break; - } - - if (mState.mSelectionStart != oldSelStart || - mState.mSelectionEnd != oldSelEnd) - mCursorPositionChanged = true; + EnsureCursorVisible(); + } } -void TextEditor::SetTabSize(int aValue) -{ - mTabSize = std::max(0, std::min(32, aValue)); +static bool IsUTFSequence(char c) { + return (c & 0xC0) == 0x80; } -void TextEditor::InsertText(const std::string & aValue) -{ - InsertText(aValue.c_str()); +void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) { + if (mLines.empty()) + return; + + auto oldPos = mState.mCursorPosition; + mState.mCursorPosition = GetActualCursorCoordinates(); + auto line = mState.mCursorPosition.mLine; + auto cindex = GetCharacterIndex(mState.mCursorPosition); + + while (aAmount-- > 0) { + if (cindex == 0) { + if (line > 0) { + --line; + if ((int)mLines.size() > line) + cindex = (int)mLines[line].size(); + else + cindex = 0; + } + } else { + --cindex; + if (cindex > 0) { + if ((int)mLines.size() > line) { + while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar)) + --cindex; + } + } + } + + mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); + if (aWordMode) { + mState.mCursorPosition = FindWordStart(mState.mCursorPosition); + cindex = GetCharacterIndex(mState.mCursorPosition); + } + } + + mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); + + assert(mState.mCursorPosition.mColumn >= 0); + if (aSelect) { + if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else { + mInteractiveStart = mState.mCursorPosition; + mInteractiveEnd = oldPos; + } + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); + + EnsureCursorVisible(); } -void TextEditor::InsertText(const char * aValue) -{ - if (aValue == nullptr) - return; +void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) { + auto oldPos = mState.mCursorPosition; - auto pos = GetActualCursorCoordinates(); - auto start = std::min(pos, mState.mSelectionStart); - int totalLines = pos.mLine - start.mLine; + if (mLines.empty() || oldPos.mLine >= mLines.size()) + return; - totalLines += InsertTextAt(pos, aValue); + auto cindex = GetCharacterIndex(mState.mCursorPosition); + while (aAmount-- > 0) { + auto lindex = mState.mCursorPosition.mLine; + auto &line = mLines[lindex]; - SetSelection(pos, pos); - SetCursorPosition(pos); - Colorize(start.mLine - 1, totalLines + 2); + if (cindex >= line.size()) { + if (mState.mCursorPosition.mLine < mLines.size() - 1) { + mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + 1)); + mState.mCursorPosition.mColumn = 0; + } else + return; + } else { + cindex += UTF8CharLength(line[cindex].mChar); + mState.mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex)); + if (aWordMode) + mState.mCursorPosition = FindNextWord(mState.mCursorPosition); + } + } + + if (aSelect) { + if (oldPos == mInteractiveEnd) + mInteractiveEnd = SanitizeCoordinates(mState.mCursorPosition); + else if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else { + mInteractiveStart = oldPos; + mInteractiveEnd = mState.mCursorPosition; + } + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); + + EnsureCursorVisible(); } -void TextEditor::DeleteSelection() -{ - assert(mState.mSelectionEnd >= mState.mSelectionStart); +void TextEditor::MoveTop(bool aSelect) { + auto oldPos = mState.mCursorPosition; + SetCursorPosition(Coordinates(0, 0)); - if (mState.mSelectionEnd == mState.mSelectionStart) - return; - - DeleteRange(mState.mSelectionStart, mState.mSelectionEnd); - - SetSelection(mState.mSelectionStart, mState.mSelectionStart); - SetCursorPosition(mState.mSelectionStart); - Colorize(mState.mSelectionStart.mLine, 1); + if (mState.mCursorPosition != oldPos) { + if (aSelect) { + mInteractiveEnd = oldPos; + mInteractiveStart = mState.mCursorPosition; + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + } } -void TextEditor::MoveUp(int aAmount, bool aSelect) -{ - auto oldPos = mState.mCursorPosition; - mState.mCursorPosition.mLine = std::max(0, mState.mCursorPosition.mLine - aAmount); - if (oldPos != mState.mCursorPosition) - { - if (aSelect) - { - if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else - { - mInteractiveStart = mState.mCursorPosition; - mInteractiveEnd = oldPos; - } - } - else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); - - EnsureCursorVisible(); - } +void TextEditor::TextEditor::MoveBottom(bool aSelect) { + auto oldPos = GetCursorPosition(); + auto newPos = Coordinates((int)mLines.size() - 1, 0); + SetCursorPosition(newPos); + if (aSelect) { + mInteractiveStart = oldPos; + mInteractiveEnd = newPos; + } else + mInteractiveStart = mInteractiveEnd = newPos; + SetSelection(mInteractiveStart, mInteractiveEnd); } -void TextEditor::MoveDown(int aAmount, bool aSelect) -{ - assert(mState.mCursorPosition.mColumn >= 0); - auto oldPos = mState.mCursorPosition; - mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + aAmount)); +void TextEditor::MoveHome(bool aSelect) { + auto oldPos = mState.mCursorPosition; + SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, 0)); - if (mState.mCursorPosition != oldPos) - { - if (aSelect) - { - if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else - { - mInteractiveStart = oldPos; - mInteractiveEnd = mState.mCursorPosition; - } - } - else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); - - EnsureCursorVisible(); - } + if (mState.mCursorPosition != oldPos) { + if (aSelect) { + if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else { + mInteractiveStart = mState.mCursorPosition; + mInteractiveEnd = oldPos; + } + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + } } -static bool IsUTFSequence(char c) -{ - return (c & 0xC0) == 0x80; +void TextEditor::MoveEnd(bool aSelect) { + auto oldPos = mState.mCursorPosition; + SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, GetLineMaxColumn(oldPos.mLine))); + + if (mState.mCursorPosition != oldPos) { + if (aSelect) { + if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else { + mInteractiveStart = oldPos; + mInteractiveEnd = mState.mCursorPosition; + } + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + } } -void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) -{ - if (mLines.empty()) - return; +void TextEditor::Delete() { + assert(!mReadOnly); - auto oldPos = mState.mCursorPosition; - mState.mCursorPosition = GetActualCursorCoordinates(); - auto line = mState.mCursorPosition.mLine; - auto cindex = GetCharacterIndex(mState.mCursorPosition); + if (mLines.empty()) + return; - while (aAmount-- > 0) - { - if (cindex == 0) - { - if (line > 0) - { - --line; - if ((int)mLines.size() > line) - cindex = (int)mLines[line].size(); - else - cindex = 0; - } - } - else - { - --cindex; - if (cindex > 0) - { - if ((int)mLines.size() > line) - { - while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar)) - --cindex; - } - } - } + UndoRecord u; + u.mBefore = mState; - mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); - if (aWordMode) - { - mState.mCursorPosition = FindWordStart(mState.mCursorPosition); - cindex = GetCharacterIndex(mState.mCursorPosition); - } - } + if (HasSelection()) { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; - mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); + DeleteSelection(); + } else { + auto pos = GetActualCursorCoordinates(); + SetCursorPosition(pos); + auto &line = mLines[pos.mLine]; - assert(mState.mCursorPosition.mColumn >= 0); - if (aSelect) - { - if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else - { - mInteractiveStart = mState.mCursorPosition; - mInteractiveEnd = oldPos; - } - } - else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); + if (pos.mColumn == GetLineMaxColumn(pos.mLine)) { + if (pos.mLine == (int)mLines.size() - 1) + return; - EnsureCursorVisible(); + u.mRemoved = '\n'; + u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); + Advance(u.mRemovedEnd); + + auto &nextLine = mLines[pos.mLine + 1]; + line.insert(line.end(), nextLine.begin(), nextLine.end()); + RemoveLine(pos.mLine + 1); + } else { + auto cindex = GetCharacterIndex(pos); + u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); + u.mRemovedEnd.mColumn++; + u.mRemoved = GetText(u.mRemovedStart, u.mRemovedEnd); + + auto d = UTF8CharLength(line[cindex].mChar); + while (d-- > 0 && cindex < (int)line.size()) + line.erase(line.begin() + cindex); + } + + mTextChanged = true; + + Colorize(pos.mLine, 1); + } + + u.mAfter = mState; + AddUndo(u); } -void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) -{ - auto oldPos = mState.mCursorPosition; +void TextEditor::Backspace() { + assert(!mReadOnly); - if (mLines.empty() || oldPos.mLine >= mLines.size()) - return; + if (mLines.empty()) + return; - auto cindex = GetCharacterIndex(mState.mCursorPosition); - while (aAmount-- > 0) - { - auto lindex = mState.mCursorPosition.mLine; - auto& line = mLines[lindex]; + UndoRecord u; + u.mBefore = mState; - if (cindex >= line.size()) - { - if (mState.mCursorPosition.mLine < mLines.size() - 1) - { - mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + 1)); - mState.mCursorPosition.mColumn = 0; - } - else - return; - } - else - { - cindex += UTF8CharLength(line[cindex].mChar); - mState.mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex)); - if (aWordMode) - mState.mCursorPosition = FindNextWord(mState.mCursorPosition); - } - } + if (HasSelection()) { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; - if (aSelect) - { - if (oldPos == mInteractiveEnd) - mInteractiveEnd = SanitizeCoordinates(mState.mCursorPosition); - else if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else - { - mInteractiveStart = oldPos; - mInteractiveEnd = mState.mCursorPosition; - } - } - else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); + DeleteSelection(); + } else { + auto pos = GetActualCursorCoordinates(); + SetCursorPosition(pos); - EnsureCursorVisible(); -} + if (mState.mCursorPosition.mColumn == 0) { + if (mState.mCursorPosition.mLine == 0) + return; -void TextEditor::MoveTop(bool aSelect) -{ - auto oldPos = mState.mCursorPosition; - SetCursorPosition(Coordinates(0, 0)); + u.mRemoved = '\n'; + u.mRemovedStart = u.mRemovedEnd = Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1)); + Advance(u.mRemovedEnd); - if (mState.mCursorPosition != oldPos) - { - if (aSelect) - { - mInteractiveEnd = oldPos; - mInteractiveStart = mState.mCursorPosition; - } - else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); - } -} + auto &line = mLines[mState.mCursorPosition.mLine]; + auto &prevLine = mLines[mState.mCursorPosition.mLine - 1]; + auto prevSize = GetLineMaxColumn(mState.mCursorPosition.mLine - 1); + prevLine.insert(prevLine.end(), line.begin(), line.end()); -void TextEditor::TextEditor::MoveBottom(bool aSelect) -{ - auto oldPos = GetCursorPosition(); - auto newPos = Coordinates((int)mLines.size() - 1, 0); - SetCursorPosition(newPos); - if (aSelect) - { - mInteractiveStart = oldPos; - mInteractiveEnd = newPos; - } - else - mInteractiveStart = mInteractiveEnd = newPos; - SetSelection(mInteractiveStart, mInteractiveEnd); -} + ErrorMarkers etmp; + for (auto &i : mErrorMarkers) + etmp.insert(ErrorMarkers::value_type(i.first - 1 == mState.mCursorPosition.mLine ? i.first - 1 : i.first, i.second)); + mErrorMarkers = std::move(etmp); -void TextEditor::MoveHome(bool aSelect) -{ - auto oldPos = mState.mCursorPosition; - SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, 0)); + RemoveLine(mState.mCursorPosition.mLine); + --mState.mCursorPosition.mLine; + mState.mCursorPosition.mColumn = prevSize; + } else { + auto &line = mLines[mState.mCursorPosition.mLine]; + auto cindex = GetCharacterIndex(pos) - 1; + auto cend = cindex + 1; + while (cindex > 0 && IsUTFSequence(line[cindex].mChar)) + --cindex; - if (mState.mCursorPosition != oldPos) - { - if (aSelect) - { - if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else - { - mInteractiveStart = mState.mCursorPosition; - mInteractiveEnd = oldPos; - } - } - else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); - } -} + // if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1) + // --cindex; -void TextEditor::MoveEnd(bool aSelect) -{ - auto oldPos = mState.mCursorPosition; - SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, GetLineMaxColumn(oldPos.mLine))); - - if (mState.mCursorPosition != oldPos) - { - if (aSelect) - { - if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else - { - mInteractiveStart = oldPos; - mInteractiveEnd = mState.mCursorPosition; - } - } - else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); - } -} - -void TextEditor::Delete() -{ - assert(!mReadOnly); - - if (mLines.empty()) - return; - - UndoRecord u; - u.mBefore = mState; - - if (HasSelection()) - { - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - - DeleteSelection(); - } - else - { - auto pos = GetActualCursorCoordinates(); - SetCursorPosition(pos); - auto& line = mLines[pos.mLine]; - - if (pos.mColumn == GetLineMaxColumn(pos.mLine)) - { - if (pos.mLine == (int)mLines.size() - 1) - return; - - u.mRemoved = '\n'; - u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); - Advance(u.mRemovedEnd); - - auto& nextLine = mLines[pos.mLine + 1]; - line.insert(line.end(), nextLine.begin(), nextLine.end()); - RemoveLine(pos.mLine + 1); - } - else - { - auto cindex = GetCharacterIndex(pos); - u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); - u.mRemovedEnd.mColumn++; - u.mRemoved = GetText(u.mRemovedStart, u.mRemovedEnd); - - auto d = UTF8CharLength(line[cindex].mChar); - while (d-- > 0 && cindex < (int)line.size()) - line.erase(line.begin() + cindex); - } - - mTextChanged = true; - - Colorize(pos.mLine, 1); - } - - u.mAfter = mState; - AddUndo(u); -} - -void TextEditor::Backspace() -{ - assert(!mReadOnly); - - if (mLines.empty()) - return; - - UndoRecord u; - u.mBefore = mState; - - if (HasSelection()) - { - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - - DeleteSelection(); - } - else - { - auto pos = GetActualCursorCoordinates(); - SetCursorPosition(pos); - - if (mState.mCursorPosition.mColumn == 0) - { - if (mState.mCursorPosition.mLine == 0) - return; - - u.mRemoved = '\n'; - u.mRemovedStart = u.mRemovedEnd = Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1)); - Advance(u.mRemovedEnd); - - auto& line = mLines[mState.mCursorPosition.mLine]; - auto& prevLine = mLines[mState.mCursorPosition.mLine - 1]; - auto prevSize = GetLineMaxColumn(mState.mCursorPosition.mLine - 1); - prevLine.insert(prevLine.end(), line.begin(), line.end()); - - ErrorMarkers etmp; - for (auto& i : mErrorMarkers) - etmp.insert(ErrorMarkers::value_type(i.first - 1 == mState.mCursorPosition.mLine ? i.first - 1 : i.first, i.second)); - mErrorMarkers = std::move(etmp); - - RemoveLine(mState.mCursorPosition.mLine); - --mState.mCursorPosition.mLine; - mState.mCursorPosition.mColumn = prevSize; - } - else - { - auto& line = mLines[mState.mCursorPosition.mLine]; - auto cindex = GetCharacterIndex(pos) - 1; - auto cend = cindex + 1; - while (cindex > 0 && IsUTFSequence(line[cindex].mChar)) - --cindex; - - //if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1) - // --cindex; - - u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); - --u.mRemovedStart.mColumn; + u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); + --u.mRemovedStart.mColumn; mState.mCursorPosition.mColumn = GetCharacterColumn(mState.mCursorPosition.mLine, cindex); - while (cindex < line.size() && cend-- > cindex) - { - u.mRemoved += line[cindex].mChar; - line.erase(line.begin() + cindex); - } - } + while (cindex < line.size() && cend-- > cindex) { + u.mRemoved += line[cindex].mChar; + line.erase(line.begin() + cindex); + } + } - mTextChanged = true; + mTextChanged = true; - EnsureCursorVisible(); - Colorize(mState.mCursorPosition.mLine, 1); - } + EnsureCursorVisible(); + Colorize(mState.mCursorPosition.mLine, 1); + } - u.mAfter = mState; - AddUndo(u); + u.mAfter = mState; + AddUndo(u); } -void TextEditor::SelectWordUnderCursor() -{ - auto c = GetCursorPosition(); - SetSelection(FindWordStart(c), FindWordEnd(c)); +void TextEditor::SelectWordUnderCursor() { + auto c = GetCursorPosition(); + SetSelection(FindWordStart(c), FindWordEnd(c)); } -void TextEditor::SelectAll() -{ - SetSelection(Coordinates(0, 0), Coordinates((int)mLines.size(), 0)); +void TextEditor::SelectAll() { + SetSelection(Coordinates(0, 0), Coordinates((int)mLines.size(), 0)); } -bool TextEditor::HasSelection() const -{ - return mState.mSelectionEnd > mState.mSelectionStart; +bool TextEditor::HasSelection() const { + return mState.mSelectionEnd > mState.mSelectionStart; } -void TextEditor::Copy() -{ - if (HasSelection()) - { - ImGui::SetClipboardText(GetSelectedText().c_str()); - } - else - { - if (!mLines.empty()) - { - std::string str; - auto& line = mLines[GetActualCursorCoordinates().mLine]; - for (auto& g : line) - str.push_back(g.mChar); - ImGui::SetClipboardText(str.c_str()); - } - } +void TextEditor::Copy() { + if (HasSelection()) { + ImGui::SetClipboardText(GetSelectedText().c_str()); + } else { + if (!mLines.empty()) { + std::string str; + auto &line = mLines[GetActualCursorCoordinates().mLine]; + for (auto &g : line) + str.push_back(g.mChar); + ImGui::SetClipboardText(str.c_str()); + } + } } -void TextEditor::Cut() -{ - if (IsReadOnly()) - { - Copy(); - } - else - { - if (HasSelection()) - { - UndoRecord u; - u.mBefore = mState; - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; +void TextEditor::Cut() { + if (IsReadOnly()) { + Copy(); + } else { + if (HasSelection()) { + UndoRecord u; + u.mBefore = mState; + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; - Copy(); - DeleteSelection(); + Copy(); + DeleteSelection(); - u.mAfter = mState; - AddUndo(u); - } - } + u.mAfter = mState; + AddUndo(u); + } + } } -void TextEditor::Paste() -{ - if (IsReadOnly()) - return; +void TextEditor::Paste() { + if (IsReadOnly()) + return; - auto clipText = ImGui::GetClipboardText(); - if (clipText != nullptr && strlen(clipText) > 0) - { - UndoRecord u; - u.mBefore = mState; + auto clipText = ImGui::GetClipboardText(); + if (clipText != nullptr && strlen(clipText) > 0) { + UndoRecord u; + u.mBefore = mState; - if (HasSelection()) - { - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - DeleteSelection(); - } + if (HasSelection()) { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + DeleteSelection(); + } - u.mAdded = clipText; - u.mAddedStart = GetActualCursorCoordinates(); + u.mAdded = clipText; + u.mAddedStart = GetActualCursorCoordinates(); - InsertText(clipText); + InsertText(clipText); - u.mAddedEnd = GetActualCursorCoordinates(); - u.mAfter = mState; - AddUndo(u); - } + u.mAddedEnd = GetActualCursorCoordinates(); + u.mAfter = mState; + AddUndo(u); + } } -bool TextEditor::CanUndo() const -{ - return !mReadOnly && mUndoIndex > 0; +bool TextEditor::CanUndo() const { + return !mReadOnly && mUndoIndex > 0; } -bool TextEditor::CanRedo() const -{ - return !mReadOnly && mUndoIndex < (int)mUndoBuffer.size(); +bool TextEditor::CanRedo() const { + return !mReadOnly && mUndoIndex < (int)mUndoBuffer.size(); } -void TextEditor::Undo(int aSteps) -{ - while (CanUndo() && aSteps-- > 0) - mUndoBuffer[--mUndoIndex].Undo(this); +void TextEditor::Undo(int aSteps) { + while (CanUndo() && aSteps-- > 0) + mUndoBuffer[--mUndoIndex].Undo(this); } -void TextEditor::Redo(int aSteps) -{ - while (CanRedo() && aSteps-- > 0) - mUndoBuffer[mUndoIndex++].Redo(this); +void TextEditor::Redo(int aSteps) { + while (CanRedo() && aSteps-- > 0) + mUndoBuffer[mUndoIndex++].Redo(this); } -const TextEditor::Palette & TextEditor::GetDarkPalette() -{ - const static Palette p = { { - 0xff7f7f7f, // Default - 0xffd69c56, // Keyword - 0xff00ff00, // Number - 0xff7070e0, // String - 0xff70a0e0, // Char literal - 0xffffffff, // Punctuation - 0xff408080, // Preprocessor - 0xffaaaaaa, // Identifier - 0xff9bc64d, // Known identifier - 0xffc040a0, // Preproc identifier - 0xff206020, // Comment (single line) - 0xff406020, // Comment (multi line) - 0xff101010, // Background - 0xffe0e0e0, // Cursor - 0x80a06020, // Selection - 0x800020ff, // ErrorMarker - 0x40f08000, // Breakpoint - 0xff707000, // Line number - 0x40000000, // Current line fill - 0x40808080, // Current line fill (inactive) - 0x40a0a0a0, // Current line edge - } }; - return p; +const TextEditor::Palette &TextEditor::GetDarkPalette() { + const static Palette p = { + { + 0xff7f7f7f, // Default + 0xffd69c56, // Keyword + 0xff00ff00, // Number + 0xff7070e0, // String + 0xff70a0e0, // Char literal + 0xffffffff, // Punctuation + 0xff408080, // Preprocessor + 0xffaaaaaa, // Identifier + 0xff9bc64d, // Known identifier + 0xffc040a0, // Preproc identifier + 0xff206020, // Comment (single line) + 0xff406020, // Comment (multi line) + 0xff101010, // Background + 0xffe0e0e0, // Cursor + 0x80a06020, // Selection + 0x800020ff, // ErrorMarker + 0x40f08000, // Breakpoint + 0xff707000, // Line number + 0x40000000, // Current line fill + 0x40808080, // Current line fill (inactive) + 0x40a0a0a0, // Current line edge + } + }; + return p; } -const TextEditor::Palette & TextEditor::GetLightPalette() -{ - const static Palette p = { { - 0xff7f7f7f, // None - 0xffff0c06, // Keyword - 0xff008000, // Number - 0xff2020a0, // String - 0xff304070, // Char literal - 0xff000000, // Punctuation - 0xff406060, // Preprocessor - 0xff404040, // Identifier - 0xff606010, // Known identifier - 0xffc040a0, // Preproc identifier - 0xff205020, // Comment (single line) - 0xff405020, // Comment (multi line) - 0xffffffff, // Background - 0xff000000, // Cursor - 0x80600000, // Selection - 0xa00010ff, // ErrorMarker - 0x80f08000, // Breakpoint - 0xff505000, // Line number - 0x40000000, // Current line fill - 0x40808080, // Current line fill (inactive) - 0x40000000, // Current line edge - } }; - return p; +const TextEditor::Palette &TextEditor::GetLightPalette() { + const static Palette p = { + { + 0xff7f7f7f, // None + 0xffff0c06, // Keyword + 0xff008000, // Number + 0xff2020a0, // String + 0xff304070, // Char literal + 0xff000000, // Punctuation + 0xff406060, // Preprocessor + 0xff404040, // Identifier + 0xff606010, // Known identifier + 0xffc040a0, // Preproc identifier + 0xff205020, // Comment (single line) + 0xff405020, // Comment (multi line) + 0xffffffff, // Background + 0xff000000, // Cursor + 0x80600000, // Selection + 0xa00010ff, // ErrorMarker + 0x80f08000, // Breakpoint + 0xff505000, // Line number + 0x40000000, // Current line fill + 0x40808080, // Current line fill (inactive) + 0x40000000, // Current line edge + } + }; + return p; } -const TextEditor::Palette & TextEditor::GetRetroBluePalette() -{ - const static Palette p = { { - 0xff00ffff, // None - 0xffffff00, // Keyword - 0xff00ff00, // Number - 0xff808000, // String - 0xff808000, // Char literal - 0xffffffff, // Punctuation - 0xff008000, // Preprocessor - 0xff00ffff, // Identifier - 0xffffffff, // Known identifier - 0xffff00ff, // Preproc identifier - 0xff808080, // Comment (single line) - 0xff404040, // Comment (multi line) - 0xff800000, // Background - 0xff0080ff, // Cursor - 0x80ffff00, // Selection - 0xa00000ff, // ErrorMarker - 0x80ff8000, // Breakpoint - 0xff808000, // Line number - 0x40000000, // Current line fill - 0x40808080, // Current line fill (inactive) - 0x40000000, // Current line edge - } }; - return p; +const TextEditor::Palette &TextEditor::GetRetroBluePalette() { + const static Palette p = { + { + 0xff00ffff, // None + 0xffffff00, // Keyword + 0xff00ff00, // Number + 0xff808000, // String + 0xff808000, // Char literal + 0xffffffff, // Punctuation + 0xff008000, // Preprocessor + 0xff00ffff, // Identifier + 0xffffffff, // Known identifier + 0xffff00ff, // Preproc identifier + 0xff808080, // Comment (single line) + 0xff404040, // Comment (multi line) + 0xff800000, // Background + 0xff0080ff, // Cursor + 0x80ffff00, // Selection + 0xa00000ff, // ErrorMarker + 0x80ff8000, // Breakpoint + 0xff808000, // Line number + 0x40000000, // Current line fill + 0x40808080, // Current line fill (inactive) + 0x40000000, // Current line edge + } + }; + return p; } -std::string TextEditor::GetText() const -{ - return GetText(Coordinates(), Coordinates((int)mLines.size(), 0)); +std::string TextEditor::GetText() const { + return GetText(Coordinates(), Coordinates((int)mLines.size(), 0)); } -std::vector TextEditor::GetTextLines() const -{ - std::vector result; +std::vector TextEditor::GetTextLines() const { + std::vector result; - result.reserve(mLines.size()); + result.reserve(mLines.size()); - for (auto & line : mLines) - { - std::string text; + for (auto &line : mLines) { + std::string text; - text.resize(line.size()); + text.resize(line.size()); - for (size_t i = 0; i < line.size(); ++i) - text[i] = line[i].mChar; + for (size_t i = 0; i < line.size(); ++i) + text[i] = line[i].mChar; - result.emplace_back(std::move(text)); - } + result.emplace_back(std::move(text)); + } - return result; + return result; } -std::string TextEditor::GetSelectedText() const -{ - return GetText(mState.mSelectionStart, mState.mSelectionEnd); +std::string TextEditor::GetSelectedText() const { + return GetText(mState.mSelectionStart, mState.mSelectionEnd); } -std::string TextEditor::GetCurrentLineText()const -{ - auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine); - return GetText( - Coordinates(mState.mCursorPosition.mLine, 0), - Coordinates(mState.mCursorPosition.mLine, lineLength)); +std::string TextEditor::GetCurrentLineText() const { + auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine); + return GetText( + Coordinates(mState.mCursorPosition.mLine, 0), + Coordinates(mState.mCursorPosition.mLine, lineLength)); } -void TextEditor::ProcessInputs() -{ +void TextEditor::ProcessInputs() { } -void TextEditor::Colorize(int aFromLine, int aLines) -{ - int toLine = aLines == -1 ? (int)mLines.size() : std::min((int)mLines.size(), aFromLine + aLines); - mColorRangeMin = std::min(mColorRangeMin, aFromLine); - mColorRangeMax = std::max(mColorRangeMax, toLine); - mColorRangeMin = std::max(0, mColorRangeMin); - mColorRangeMax = std::max(mColorRangeMin, mColorRangeMax); - mCheckComments = true; +void TextEditor::Colorize(int aFromLine, int aLines) { + int toLine = aLines == -1 ? (int)mLines.size() : std::min((int)mLines.size(), aFromLine + aLines); + mColorRangeMin = std::min(mColorRangeMin, aFromLine); + mColorRangeMax = std::max(mColorRangeMax, toLine); + mColorRangeMin = std::max(0, mColorRangeMin); + mColorRangeMax = std::max(mColorRangeMin, mColorRangeMax); + mCheckComments = true; } -void TextEditor::ColorizeRange(int aFromLine, int aToLine) -{ - if (mLines.empty() || aFromLine >= aToLine) - return; +void TextEditor::ColorizeRange(int aFromLine, int aToLine) { + if (mLines.empty() || aFromLine >= aToLine) + return; - std::string buffer; - std::cmatch results; - std::string id; + std::string buffer; + std::cmatch results; + std::string id; - int endLine = std::max(0, std::min((int)mLines.size(), aToLine)); - for (int i = aFromLine; i < endLine; ++i) - { - auto& line = mLines[i]; + int endLine = std::max(0, std::min((int)mLines.size(), aToLine)); + for (int i = aFromLine; i < endLine; ++i) { + auto &line = mLines[i]; - if (line.empty()) - continue; + if (line.empty()) + continue; - buffer.resize(line.size()); - for (size_t j = 0; j < line.size(); ++j) - { - auto& col = line[j]; - buffer[j] = col.mChar; - col.mColorIndex = PaletteIndex::Default; - } + buffer.resize(line.size()); + for (size_t j = 0; j < line.size(); ++j) { + auto &col = line[j]; + buffer[j] = col.mChar; + col.mColorIndex = PaletteIndex::Default; + } - const char * bufferBegin = &buffer.front(); - const char * bufferEnd = bufferBegin + buffer.size(); + const char *bufferBegin = &buffer.front(); + const char *bufferEnd = bufferBegin + buffer.size(); - auto last = bufferEnd; + auto last = bufferEnd; - for (auto first = bufferBegin; first != last; ) - { - const char * token_begin = nullptr; - const char * token_end = nullptr; - PaletteIndex token_color = PaletteIndex::Default; + for (auto first = bufferBegin; first != last;) { + const char *token_begin = nullptr; + const char *token_end = nullptr; + PaletteIndex token_color = PaletteIndex::Default; - bool hasTokenizeResult = false; + bool hasTokenizeResult = false; - if (mLanguageDefinition.mTokenize != nullptr) - { - if (mLanguageDefinition.mTokenize(first, last, token_begin, token_end, token_color)) - hasTokenizeResult = true; - } + if (mLanguageDefinition.mTokenize != nullptr) { + if (mLanguageDefinition.mTokenize(first, last, token_begin, token_end, token_color)) + hasTokenizeResult = true; + } - if (hasTokenizeResult == false) - { - // todo : remove - //printf("using regex for %.*s\n", first + 10 < last ? 10 : int(last - first), first); + if (hasTokenizeResult == false) { + // todo : remove + // printf("using regex for %.*s\n", first + 10 < last ? 10 : int(last - first), first); - for (auto& p : mRegexList) - { - if (std::regex_search(first, last, results, p.first, std::regex_constants::match_continuous)) - { - hasTokenizeResult = true; + for (auto &p : mRegexList) { + if (std::regex_search(first, last, results, p.first, std::regex_constants::match_continuous)) { + hasTokenizeResult = true; - auto& v = *results.begin(); - token_begin = v.first; - token_end = v.second; - token_color = p.second; - break; - } - } - } + auto &v = *results.begin(); + token_begin = v.first; + token_end = v.second; + token_color = p.second; + break; + } + } + } - if (hasTokenizeResult == false) - { - first++; - } - else - { - const size_t token_length = token_end - token_begin; + if (hasTokenizeResult == false) { + first++; + } else { + const size_t token_length = token_end - token_begin; - if (token_color == PaletteIndex::Identifier) - { - id.assign(token_begin, token_end); + if (token_color == PaletteIndex::Identifier) { + id.assign(token_begin, token_end); - // todo : allmost all language definitions use lower case to specify keywords, so shouldn't this use ::tolower ? - if (!mLanguageDefinition.mCaseSensitive) - std::transform(id.begin(), id.end(), id.begin(), ::toupper); + // todo : allmost all language definitions use lower case to specify keywords, so shouldn't this use ::tolower ? + if (!mLanguageDefinition.mCaseSensitive) + std::transform(id.begin(), id.end(), id.begin(), ::toupper); - if (!line[first - bufferBegin].mPreprocessor) - { - if (mLanguageDefinition.mKeywords.count(id) != 0) - token_color = PaletteIndex::Keyword; - else if (mLanguageDefinition.mIdentifiers.count(id) != 0) - token_color = PaletteIndex::KnownIdentifier; - else if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0) - token_color = PaletteIndex::PreprocIdentifier; - } - else - { - if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0) - token_color = PaletteIndex::PreprocIdentifier; - } - } + if (!line[first - bufferBegin].mPreprocessor) { + if (mLanguageDefinition.mKeywords.count(id) != 0) + token_color = PaletteIndex::Keyword; + else if (mLanguageDefinition.mIdentifiers.count(id) != 0) + token_color = PaletteIndex::KnownIdentifier; + else if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0) + token_color = PaletteIndex::PreprocIdentifier; + } else { + if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0) + token_color = PaletteIndex::PreprocIdentifier; + } + } - for (size_t j = 0; j < token_length; ++j) - line[(token_begin - bufferBegin) + j].mColorIndex = token_color; + for (size_t j = 0; j < token_length; ++j) + line[(token_begin - bufferBegin) + j].mColorIndex = token_color; - first = token_end; - } - } - } + first = token_end; + } + } + } } -void TextEditor::ColorizeInternal() -{ - if (mLines.empty() || !mColorizerEnabled) - return; +void TextEditor::ColorizeInternal() { + if (mLines.empty() || !mColorizerEnabled) + return; - if (mCheckComments) - { - auto endLine = mLines.size(); - auto endIndex = 0; - auto commentStartLine = endLine; - auto commentStartIndex = endIndex; - auto withinString = false; - auto withinSingleLineComment = false; - auto withinPreproc = false; - auto firstChar = true; // there is no other non-whitespace characters in the line before - auto concatenate = false; // '\' on the very end of the line - auto currentLine = 0; - auto currentIndex = 0; - while (currentLine < endLine || currentIndex < endIndex) - { - auto& line = mLines[currentLine]; + if (mCheckComments) { + auto endLine = mLines.size(); + auto endIndex = 0; + auto commentStartLine = endLine; + auto commentStartIndex = endIndex; + auto withinString = false; + auto withinSingleLineComment = false; + auto withinPreproc = false; + auto firstChar = true; // there is no other non-whitespace characters in the line before + auto concatenate = false; // '\' on the very end of the line + auto currentLine = 0; + auto currentIndex = 0; + while (currentLine < endLine || currentIndex < endIndex) { + auto &line = mLines[currentLine]; - if (currentIndex == 0 && !concatenate) - { - withinSingleLineComment = false; - withinPreproc = false; - firstChar = true; - } + if (currentIndex == 0 && !concatenate) { + withinSingleLineComment = false; + withinPreproc = false; + firstChar = true; + } - concatenate = false; + concatenate = false; - if (!line.empty()) - { - auto& g = line[currentIndex]; - auto c = g.mChar; + if (!line.empty()) { + auto &g = line[currentIndex]; + auto c = g.mChar; - if (c != mLanguageDefinition.mPreprocChar && !isspace(c)) - firstChar = false; + if (c != mLanguageDefinition.mPreprocChar && !isspace(c)) + firstChar = false; - if (currentIndex == (int)line.size() - 1 && line[line.size() - 1].mChar == '\\') - concatenate = true; + if (currentIndex == (int)line.size() - 1 && line[line.size() - 1].mChar == '\\') + concatenate = true; - bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); + bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); - if (withinString) - { - line[currentIndex].mMultiLineComment = inComment; + if (withinString) { + line[currentIndex].mMultiLineComment = inComment; - if (c == '\"') - { - if (currentIndex + 1 < (int)line.size() && line[currentIndex + 1].mChar == '\"') - { - currentIndex += 1; - if (currentIndex < (int)line.size()) - line[currentIndex].mMultiLineComment = inComment; - } - else - withinString = false; - } - else if (c == '\\') - { - currentIndex += 1; - if (currentIndex < (int)line.size()) - line[currentIndex].mMultiLineComment = inComment; - } - } - else - { - if (firstChar && c == mLanguageDefinition.mPreprocChar) - withinPreproc = true; + if (c == '\"') { + if (currentIndex + 1 < (int)line.size() && line[currentIndex + 1].mChar == '\"') { + currentIndex += 1; + if (currentIndex < (int)line.size()) + line[currentIndex].mMultiLineComment = inComment; + } else + withinString = false; + } else if (c == '\\') { + currentIndex += 1; + if (currentIndex < (int)line.size()) + line[currentIndex].mMultiLineComment = inComment; + } + } else { + if (firstChar && c == mLanguageDefinition.mPreprocChar) + withinPreproc = true; - if (c == '\"') - { - withinString = true; - line[currentIndex].mMultiLineComment = inComment; - } - else - { - auto pred = [](const char& a, const Glyph& b) { return a == b.mChar; }; - auto from = line.begin() + currentIndex; - auto& startStr = mLanguageDefinition.mCommentStart; - auto& singleStartStr = mLanguageDefinition.mSingleLineComment; + if (c == '\"') { + withinString = true; + line[currentIndex].mMultiLineComment = inComment; + } else { + auto pred = [](const char &a, const Glyph &b) { return a == b.mChar; }; + auto from = line.begin() + currentIndex; + auto &startStr = mLanguageDefinition.mCommentStart; + auto &singleStartStr = mLanguageDefinition.mSingleLineComment; - if (singleStartStr.size() > 0 && - currentIndex + singleStartStr.size() <= line.size() && - equals(singleStartStr.begin(), singleStartStr.end(), from, from + singleStartStr.size(), pred)) - { - withinSingleLineComment = true; - } - else if (!withinSingleLineComment && currentIndex + startStr.size() <= line.size() && - equals(startStr.begin(), startStr.end(), from, from + startStr.size(), pred)) - { - commentStartLine = currentLine; - commentStartIndex = currentIndex; - } + if (singleStartStr.size() > 0 && + currentIndex + singleStartStr.size() <= line.size() && + equals(singleStartStr.begin(), singleStartStr.end(), from, from + singleStartStr.size(), pred)) { + withinSingleLineComment = true; + } else if (!withinSingleLineComment && currentIndex + startStr.size() <= line.size() && + equals(startStr.begin(), startStr.end(), from, from + startStr.size(), pred)) { + commentStartLine = currentLine; + commentStartIndex = currentIndex; + } - inComment = inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); + inComment = inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); - line[currentIndex].mMultiLineComment = inComment; - line[currentIndex].mComment = withinSingleLineComment; + line[currentIndex].mMultiLineComment = inComment; + line[currentIndex].mComment = withinSingleLineComment; - auto& endStr = mLanguageDefinition.mCommentEnd; - if (currentIndex + 1 >= (int)endStr.size() && - equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(), from + 1, pred)) - { - commentStartIndex = endIndex; - commentStartLine = endLine; - } - } - } - line[currentIndex].mPreprocessor = withinPreproc; - currentIndex += UTF8CharLength(c); - if (currentIndex >= (int)line.size()) - { - currentIndex = 0; - ++currentLine; - } - } - else - { - currentIndex = 0; - ++currentLine; - } - } - mCheckComments = false; - } + auto &endStr = mLanguageDefinition.mCommentEnd; + if (currentIndex + 1 >= (int)endStr.size() && + equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(), from + 1, pred)) { + commentStartIndex = endIndex; + commentStartLine = endLine; + } + } + } + line[currentIndex].mPreprocessor = withinPreproc; + currentIndex += UTF8CharLength(c); + if (currentIndex >= (int)line.size()) { + currentIndex = 0; + ++currentLine; + } + } else { + currentIndex = 0; + ++currentLine; + } + } + mCheckComments = false; + } - if (mColorRangeMin < mColorRangeMax) - { - const int increment = (mLanguageDefinition.mTokenize == nullptr) ? 10 : 10000; - const int to = std::min(mColorRangeMin + increment, mColorRangeMax); - ColorizeRange(mColorRangeMin, to); - mColorRangeMin = to; + if (mColorRangeMin < mColorRangeMax) { + const int increment = (mLanguageDefinition.mTokenize == nullptr) ? 10 : 10000; + const int to = std::min(mColorRangeMin + increment, mColorRangeMax); + ColorizeRange(mColorRangeMin, to); + mColorRangeMin = to; - if (mColorRangeMax == mColorRangeMin) - { - mColorRangeMin = std::numeric_limits::max(); - mColorRangeMax = 0; - } - return; - } + if (mColorRangeMax == mColorRangeMin) { + mColorRangeMin = std::numeric_limits::max(); + mColorRangeMax = 0; + } + return; + } } -float TextEditor::TextDistanceToLineStart(const Coordinates& aFrom) const -{ - auto& line = mLines[aFrom.mLine]; - float distance = 0.0f; - float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x; - int colIndex = GetCharacterIndex(aFrom); - for (size_t it = 0u; it < line.size() && it < colIndex; ) - { - if (line[it].mChar == '\t') - { - distance = (1.0f + std::floor((1.0f + distance) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); - ++it; - } - else - { - auto d = UTF8CharLength(line[it].mChar); - char tempCString[7]; - int i = 0; - for (; i < 6 && d-- > 0 && it < (int)line.size(); i++, it++) - tempCString[i] = line[it].mChar; +float TextEditor::TextDistanceToLineStart(const Coordinates &aFrom) const { + auto &line = mLines[aFrom.mLine]; + float distance = 0.0f; + float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x; + int colIndex = GetCharacterIndex(aFrom); + for (size_t it = 0u; it < line.size() && it < colIndex;) { + if (line[it].mChar == '\t') { + distance = (1.0f + std::floor((1.0f + distance) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); + ++it; + } else { + auto d = UTF8CharLength(line[it].mChar); + char tempCString[7]; + int i = 0; + for (; i < 6 && d-- > 0 && it < (int)line.size(); i++, it++) + tempCString[i] = line[it].mChar; - tempCString[i] = '\0'; - distance += ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, tempCString, nullptr, nullptr).x; - } - } + tempCString[i] = '\0'; + distance += ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, tempCString, nullptr, nullptr).x; + } + } - return distance; + return distance; } -void TextEditor::EnsureCursorVisible() -{ - if (!mWithinRender) - { - mScrollToCursor = true; - return; - } +void TextEditor::EnsureCursorVisible() { + if (!mWithinRender) { + mScrollToCursor = true; + return; + } - float scrollX = ImGui::GetScrollX(); - float scrollY = ImGui::GetScrollY(); + float scrollX = ImGui::GetScrollX(); + float scrollY = ImGui::GetScrollY(); - auto height = ImGui::GetWindowHeight(); - auto width = ImGui::GetWindowWidth(); + auto height = ImGui::GetWindowHeight(); + auto width = ImGui::GetWindowWidth(); - auto top = 1 + (int)ceil(scrollY / mCharAdvance.y); - auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y); + auto top = 1 + (int)ceil(scrollY / mCharAdvance.y); + auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y); - auto left = (int)ceil(scrollX / mCharAdvance.x); - auto right = (int)ceil((scrollX + width) / mCharAdvance.x); + auto left = (int)ceil(scrollX / mCharAdvance.x); + auto right = (int)ceil((scrollX + width) / mCharAdvance.x); - auto pos = GetActualCursorCoordinates(); - auto len = TextDistanceToLineStart(pos); + auto pos = GetActualCursorCoordinates(); + auto len = TextDistanceToLineStart(pos); - if (pos.mLine < top) - ImGui::SetScrollY(std::max(0.0f, (pos.mLine - 1) * mCharAdvance.y)); - if (pos.mLine > bottom - 4) - ImGui::SetScrollY(std::max(0.0f, (pos.mLine + 4) * mCharAdvance.y - height)); - if (len + mTextStart < left + 4) - ImGui::SetScrollX(std::max(0.0f, len + mTextStart - 4)); - if (len + mTextStart > right - 4) - ImGui::SetScrollX(std::max(0.0f, len + mTextStart + 4 - width)); + if (pos.mLine < top) + ImGui::SetScrollY(std::max(0.0f, (pos.mLine - 1) * mCharAdvance.y)); + if (pos.mLine > bottom - 4) + ImGui::SetScrollY(std::max(0.0f, (pos.mLine + 4) * mCharAdvance.y - height)); + if (len + mTextStart < left + 4) + ImGui::SetScrollX(std::max(0.0f, len + mTextStart - 4)); + if (len + mTextStart > right - 4) + ImGui::SetScrollX(std::max(0.0f, len + mTextStart + 4 - width)); } -int TextEditor::GetPageSize() const -{ - auto height = ImGui::GetWindowHeight() - 20.0f; - return (int)floor(height / mCharAdvance.y); +int TextEditor::GetPageSize() const { + auto height = ImGui::GetWindowHeight() - 20.0f; + return (int)floor(height / mCharAdvance.y); } TextEditor::UndoRecord::UndoRecord( - const std::string& aAdded, - const TextEditor::Coordinates aAddedStart, - const TextEditor::Coordinates aAddedEnd, - const std::string& aRemoved, - const TextEditor::Coordinates aRemovedStart, - const TextEditor::Coordinates aRemovedEnd, - TextEditor::EditorState& aBefore, - TextEditor::EditorState& aAfter) - : mAdded(aAdded) - , mAddedStart(aAddedStart) - , mAddedEnd(aAddedEnd) - , mRemoved(aRemoved) - , mRemovedStart(aRemovedStart) - , mRemovedEnd(aRemovedEnd) - , mBefore(aBefore) - , mAfter(aAfter) -{ - assert(mAddedStart <= mAddedEnd); - assert(mRemovedStart <= mRemovedEnd); + const std::string &aAdded, + const TextEditor::Coordinates aAddedStart, + const TextEditor::Coordinates aAddedEnd, + const std::string &aRemoved, + const TextEditor::Coordinates aRemovedStart, + const TextEditor::Coordinates aRemovedEnd, + TextEditor::EditorState &aBefore, + TextEditor::EditorState &aAfter) + : mAdded(aAdded), mAddedStart(aAddedStart), mAddedEnd(aAddedEnd), mRemoved(aRemoved), mRemovedStart(aRemovedStart), mRemovedEnd(aRemovedEnd), mBefore(aBefore), mAfter(aAfter) { + assert(mAddedStart <= mAddedEnd); + assert(mRemovedStart <= mRemovedEnd); } -void TextEditor::UndoRecord::Undo(TextEditor * aEditor) -{ - if (!mAdded.empty()) - { - aEditor->DeleteRange(mAddedStart, mAddedEnd); - aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 2); - } +void TextEditor::UndoRecord::Undo(TextEditor *aEditor) { + if (!mAdded.empty()) { + aEditor->DeleteRange(mAddedStart, mAddedEnd); + aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 2); + } - if (!mRemoved.empty()) - { - auto start = mRemovedStart; - aEditor->InsertTextAt(start, mRemoved.c_str()); - aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 2); - } - - aEditor->mState = mBefore; - aEditor->EnsureCursorVisible(); + if (!mRemoved.empty()) { + auto start = mRemovedStart; + aEditor->InsertTextAt(start, mRemoved.c_str()); + aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 2); + } + aEditor->mState = mBefore; + aEditor->EnsureCursorVisible(); } -void TextEditor::UndoRecord::Redo(TextEditor * aEditor) -{ - if (!mRemoved.empty()) - { - aEditor->DeleteRange(mRemovedStart, mRemovedEnd); - aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 1); - } +void TextEditor::UndoRecord::Redo(TextEditor *aEditor) { + if (!mRemoved.empty()) { + aEditor->DeleteRange(mRemovedStart, mRemovedEnd); + aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 1); + } - if (!mAdded.empty()) - { - auto start = mAddedStart; - aEditor->InsertTextAt(start, mAdded.c_str()); - aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 1); - } + if (!mAdded.empty()) { + auto start = mAddedStart; + aEditor->InsertTextAt(start, mAdded.c_str()); + aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 1); + } - aEditor->mState = mAfter; - aEditor->EnsureCursorVisible(); + aEditor->mState = mAfter; + aEditor->EnsureCursorVisible(); } -bool TokenizeCStyleString(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end) -{ - const char * p = in_begin; +bool TokenizeCStyleString(const char *in_begin, const char *in_end, const char *&out_begin, const char *&out_end) { + const char *p = in_begin; - if (*p == '"') - { - p++; + if (*p == '"') { + p++; - while (p < in_end) - { - // handle end of string - if (*p == '"') - { - out_begin = in_begin; - out_end = p + 1; - return true; - } + while (p < in_end) { + // handle end of string + if (*p == '"') { + out_begin = in_begin; + out_end = p + 1; + return true; + } - // handle escape character for " - if (*p == '\\' && p + 1 < in_end && p[1] == '"') - p++; + // handle escape character for " + if (*p == '\\' && p + 1 < in_end && p[1] == '"') + p++; - p++; - } - } + p++; + } + } - return false; + return false; } -bool TokenizeCStyleCharacterLiteral(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end) -{ - const char * p = in_begin; +bool TokenizeCStyleCharacterLiteral(const char *in_begin, const char *in_end, const char *&out_begin, const char *&out_end) { + const char *p = in_begin; - if (*p == '\'') - { - p++; + if (*p == '\'') { + p++; - // handle escape characters - if (p < in_end && *p == '\\') - p++; + // handle escape characters + if (p < in_end && *p == '\\') + p++; - if (p < in_end) - p++; + if (p < in_end) + p++; - // handle end of character literal - if (p < in_end && *p == '\'') - { - out_begin = in_begin; - out_end = p + 1; - return true; - } - } + // handle end of character literal + if (p < in_end && *p == '\'') { + out_begin = in_begin; + out_end = p + 1; + return true; + } + } - return false; + return false; } -bool TokenizeCStyleIdentifier(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end) -{ - const char * p = in_begin; +bool TokenizeCStyleIdentifier(const char *in_begin, const char *in_end, const char *&out_begin, const char *&out_end) { + const char *p = in_begin; - if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_') - { - p++; + if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_') { + p++; - while ((p < in_end) && ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_')) - p++; + while ((p < in_end) && ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_')) + p++; - out_begin = in_begin; - out_end = p; - return true; - } + out_begin = in_begin; + out_end = p; + return true; + } - return false; + return false; } -bool TokenizeCStyleNumber(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end) -{ - const char * p = in_begin; +bool TokenizeCStyleNumber(const char *in_begin, const char *in_end, const char *&out_begin, const char *&out_end) { + const char *p = in_begin; - const bool startsWithNumber = *p >= '0' && *p <= '9'; + const bool startsWithNumber = *p >= '0' && *p <= '9'; - if (*p != '+' && *p != '-' && !startsWithNumber) - return false; + if (*p != '+' && *p != '-' && !startsWithNumber) + return false; - p++; + p++; - bool hasNumber = startsWithNumber; + bool hasNumber = startsWithNumber; - while (p < in_end && (*p >= '0' && *p <= '9')) - { - hasNumber = true; + while (p < in_end && (*p >= '0' && *p <= '9')) { + hasNumber = true; - p++; - } + p++; + } - if (hasNumber == false) - return false; + if (hasNumber == false) + return false; - bool isFloat = false; - bool isHex = false; - bool isBinary = false; + bool isFloat = false; + bool isHex = false; + bool isBinary = false; - if (p < in_end) - { - if (*p == '.') - { - isFloat = true; + if (p < in_end) { + if (*p == '.') { + isFloat = true; - p++; + p++; - while (p < in_end && (*p >= '0' && *p <= '9')) - p++; - } - else if (*p == 'x' || *p == 'X') - { - // hex formatted integer of the type 0xef80 + while (p < in_end && (*p >= '0' && *p <= '9')) + p++; + } else if (*p == 'x' || *p == 'X') { + // hex formatted integer of the type 0xef80 - isHex = true; + isHex = true; - p++; + p++; - while (p < in_end && ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F'))) - p++; - } - else if (*p == 'b' || *p == 'B') - { - // binary formatted integer of the type 0b01011101 + while (p < in_end && ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F') || *p == '.' || *p == 'p' || *p == 'P')) + p++; + } else if (*p == 'b' || *p == 'B') { + // binary formatted integer of the type 0b01011101 - isBinary = true; + isBinary = true; - p++; + p++; - while (p < in_end && (*p >= '0' && *p <= '1')) - p++; - } - } + while (p < in_end && (*p >= '0' && *p <= '1')) + p++; + } + } - if (isHex == false && isBinary == false) - { - // floating point exponent - if (p < in_end && (*p == 'e' || *p == 'E')) - { - isFloat = true; + if (isHex == false && isBinary == false) { + // floating point exponent + if (p < in_end && (*p == 'e' || *p == 'E')) { + isFloat = true; - p++; + p++; - if (p < in_end && (*p == '+' || *p == '-')) - p++; + if (p < in_end && (*p == '+' || *p == '-')) + p++; - bool hasDigits = false; + bool hasDigits = false; - while (p < in_end && (*p >= '0' && *p <= '9')) - { - hasDigits = true; + while (p < in_end && (*p >= '0' && *p <= '9')) { + hasDigits = true; - p++; - } + p++; + } - if (hasDigits == false) - return false; - } + if (hasDigits == false) + return false; + } - // single precision floating point type - if (p < in_end && *p == 'f') - p++; - } + // single precision floating point type + if (p < in_end && *p == 'f') + p++; + } - if (isFloat == false) - { - // integer size type - while (p < in_end && (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L')) - p++; - } + if (isFloat == false) { + // integer size type + while (p < in_end && (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L')) + p++; + } - out_begin = in_begin; - out_end = p; - return true; + out_begin = in_begin; + out_end = p; + return true; } -bool TokenizeCStylePunctuation(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end) -{ - (void)in_end; +bool TokenizeCStylePunctuation(const char *in_begin, const char *in_end, const char *&out_begin, const char *&out_end) { + (void)in_end; - switch (*in_begin) - { - case '[': - case ']': - case '{': - case '}': - case '!': - case '%': - case '^': - case '&': - case '*': - case '(': - case ')': - case '-': - case '+': - case '=': - case '~': - case '|': - case '<': - case '>': - case '?': - case ':': - case '/': - case ';': - case ',': - case '.': - out_begin = in_begin; - out_end = in_begin + 1; - return true; - } + switch (*in_begin) { + case '[': + case ']': + case '{': + case '}': + case '!': + case '%': + case '^': + case '&': + case '*': + case '(': + case ')': + case '-': + case '+': + case '=': + case '~': + case '|': + case '<': + case '>': + case '?': + case ':': + case '/': + case ';': + case ',': + case '.': + out_begin = in_begin; + out_end = in_begin + 1; + return true; + } - return false; + return false; } -const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::CPlusPlus() -{ - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) - { - static const char* const cppKeywords[] = { - "alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit", "atomic_noexcept", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class", - "compl", "concept", "const", "constexpr", "const_cast", "continue", "decltype", "default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float", - "for", "friend", "goto", "if", "import", "inline", "int", "long", "module", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", "public", - "register", "reinterpret_cast", "requires", "return", "short", "signed", "sizeof", "static", "static_assert", "static_cast", "struct", "switch", "synchronized", "template", "this", "thread_local", - "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq" - }; - for (auto& k : cppKeywords) - langDef.mKeywords.insert(k); +const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::CPlusPlus() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const cppKeywords[] = { + "alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit", "atomic_noexcept", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class", "compl", "concept", "const", "constexpr", "const_cast", "continue", "decltype", "default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float", "for", "friend", "goto", "if", "import", "inline", "int", "long", "module", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", "public", "register", "reinterpret_cast", "requires", "return", "short", "signed", "sizeof", "static", "static_assert", "static_cast", "struct", "switch", "synchronized", "template", "this", "thread_local", "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq" + }; + for (auto &k : cppKeywords) + langDef.mKeywords.insert(k); - static const char* const identifiers[] = { - "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", - "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "printf", "sprintf", "snprintf", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper", - "std", "string", "vector", "map", "unordered_map", "set", "unordered_set", "min", "max" - }; - for (auto& k : identifiers) - { - Identifier id; - id.mDeclaration = "Built-in function"; - langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); - } + static const char *const identifiers[] = { + "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "printf", "sprintf", "snprintf", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper", "std", "string", "vector", "map", "unordered_map", "set", "unordered_set", "min", "max" + }; + for (auto &k : identifiers) { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } - langDef.mTokenize = [](const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end, PaletteIndex & paletteIndex) -> bool - { - paletteIndex = PaletteIndex::Max; + langDef.mTokenize = [](const char *in_begin, const char *in_end, const char *&out_begin, const char *&out_end, PaletteIndex &paletteIndex) -> bool { + paletteIndex = PaletteIndex::Max; - while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin)) - in_begin++; + while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin)) + in_begin++; - if (in_begin == in_end) - { - out_begin = in_end; - out_end = in_end; - paletteIndex = PaletteIndex::Default; - } - else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::String; - else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::CharLiteral; - else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::Identifier; - else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::Number; - else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::Punctuation; + if (in_begin == in_end) { + out_begin = in_end; + out_end = in_end; + paletteIndex = PaletteIndex::Default; + } else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::String; + else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::CharLiteral; + else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Identifier; + else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Number; + else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Punctuation; - return paletteIndex != PaletteIndex::Max; - }; + return paletteIndex != PaletteIndex::Max; + }; - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - langDef.mSingleLineComment = "//"; + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; - langDef.mCaseSensitive = true; - langDef.mAutoIndentation = true; + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = true; - langDef.mName = "C++"; + langDef.mName = "C++"; - inited = true; - } - return langDef; + inited = true; + } + return langDef; } -const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::HLSL() -{ - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) - { - static const char* const keywords[] = { - "AppendStructuredBuffer", "asm", "asm_fragment", "BlendState", "bool", "break", "Buffer", "ByteAddressBuffer", "case", "cbuffer", "centroid", "class", "column_major", "compile", "compile_fragment", - "CompileShader", "const", "continue", "ComputeShader", "ConsumeStructuredBuffer", "default", "DepthStencilState", "DepthStencilView", "discard", "do", "double", "DomainShader", "dword", "else", - "export", "extern", "false", "float", "for", "fxgroup", "GeometryShader", "groupshared", "half", "Hullshader", "if", "in", "inline", "inout", "InputPatch", "int", "interface", "line", "lineadj", - "linear", "LineStream", "matrix", "min16float", "min10float", "min16int", "min12int", "min16uint", "namespace", "nointerpolation", "noperspective", "NULL", "out", "OutputPatch", "packoffset", - "pass", "pixelfragment", "PixelShader", "point", "PointStream", "precise", "RasterizerState", "RenderTargetView", "return", "register", "row_major", "RWBuffer", "RWByteAddressBuffer", "RWStructuredBuffer", - "RWTexture1D", "RWTexture1DArray", "RWTexture2D", "RWTexture2DArray", "RWTexture3D", "sample", "sampler", "SamplerState", "SamplerComparisonState", "shared", "snorm", "stateblock", "stateblock_state", - "static", "string", "struct", "switch", "StructuredBuffer", "tbuffer", "technique", "technique10", "technique11", "texture", "Texture1D", "Texture1DArray", "Texture2D", "Texture2DArray", "Texture2DMS", - "Texture2DMSArray", "Texture3D", "TextureCube", "TextureCubeArray", "true", "typedef", "triangle", "triangleadj", "TriangleStream", "uint", "uniform", "unorm", "unsigned", "vector", "vertexfragment", - "VertexShader", "void", "volatile", "while", - "bool1","bool2","bool3","bool4","double1","double2","double3","double4", "float1", "float2", "float3", "float4", "int1", "int2", "int3", "int4", "in", "out", "inout", - "uint1", "uint2", "uint3", "uint4", "dword1", "dword2", "dword3", "dword4", "half1", "half2", "half3", "half4", - "float1x1","float2x1","float3x1","float4x1","float1x2","float2x2","float3x2","float4x2", - "float1x3","float2x3","float3x3","float4x3","float1x4","float2x4","float3x4","float4x4", - "half1x1","half2x1","half3x1","half4x1","half1x2","half2x2","half3x2","half4x2", - "half1x3","half2x3","half3x3","half4x3","half1x4","half2x4","half3x4","half4x4", - }; - for (auto& k : keywords) - langDef.mKeywords.insert(k); +const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::HLSL() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const keywords[] = { + "AppendStructuredBuffer", + "asm", + "asm_fragment", + "BlendState", + "bool", + "break", + "Buffer", + "ByteAddressBuffer", + "case", + "cbuffer", + "centroid", + "class", + "column_major", + "compile", + "compile_fragment", + "CompileShader", + "const", + "continue", + "ComputeShader", + "ConsumeStructuredBuffer", + "default", + "DepthStencilState", + "DepthStencilView", + "discard", + "do", + "double", + "DomainShader", + "dword", + "else", + "export", + "extern", + "false", + "float", + "for", + "fxgroup", + "GeometryShader", + "groupshared", + "half", + "Hullshader", + "if", + "in", + "inline", + "inout", + "InputPatch", + "int", + "interface", + "line", + "lineadj", + "linear", + "LineStream", + "matrix", + "min16float", + "min10float", + "min16int", + "min12int", + "min16uint", + "namespace", + "nointerpolation", + "noperspective", + "NULL", + "out", + "OutputPatch", + "packoffset", + "pass", + "pixelfragment", + "PixelShader", + "point", + "PointStream", + "precise", + "RasterizerState", + "RenderTargetView", + "return", + "register", + "row_major", + "RWBuffer", + "RWByteAddressBuffer", + "RWStructuredBuffer", + "RWTexture1D", + "RWTexture1DArray", + "RWTexture2D", + "RWTexture2DArray", + "RWTexture3D", + "sample", + "sampler", + "SamplerState", + "SamplerComparisonState", + "shared", + "snorm", + "stateblock", + "stateblock_state", + "static", + "string", + "struct", + "switch", + "StructuredBuffer", + "tbuffer", + "technique", + "technique10", + "technique11", + "texture", + "Texture1D", + "Texture1DArray", + "Texture2D", + "Texture2DArray", + "Texture2DMS", + "Texture2DMSArray", + "Texture3D", + "TextureCube", + "TextureCubeArray", + "true", + "typedef", + "triangle", + "triangleadj", + "TriangleStream", + "uint", + "uniform", + "unorm", + "unsigned", + "vector", + "vertexfragment", + "VertexShader", + "void", + "volatile", + "while", + "bool1", + "bool2", + "bool3", + "bool4", + "double1", + "double2", + "double3", + "double4", + "float1", + "float2", + "float3", + "float4", + "int1", + "int2", + "int3", + "int4", + "in", + "out", + "inout", + "uint1", + "uint2", + "uint3", + "uint4", + "dword1", + "dword2", + "dword3", + "dword4", + "half1", + "half2", + "half3", + "half4", + "float1x1", + "float2x1", + "float3x1", + "float4x1", + "float1x2", + "float2x2", + "float3x2", + "float4x2", + "float1x3", + "float2x3", + "float3x3", + "float4x3", + "float1x4", + "float2x4", + "float3x4", + "float4x4", + "half1x1", + "half2x1", + "half3x1", + "half4x1", + "half1x2", + "half2x2", + "half3x2", + "half4x2", + "half1x3", + "half2x3", + "half3x3", + "half4x3", + "half1x4", + "half2x4", + "half3x4", + "half4x4", + }; + for (auto &k : keywords) + langDef.mKeywords.insert(k); - static const char* const identifiers[] = { - "abort", "abs", "acos", "all", "AllMemoryBarrier", "AllMemoryBarrierWithGroupSync", "any", "asdouble", "asfloat", "asin", "asint", "asint", "asuint", - "asuint", "atan", "atan2", "ceil", "CheckAccessFullyMapped", "clamp", "clip", "cos", "cosh", "countbits", "cross", "D3DCOLORtoUBYTE4", "ddx", - "ddx_coarse", "ddx_fine", "ddy", "ddy_coarse", "ddy_fine", "degrees", "determinant", "DeviceMemoryBarrier", "DeviceMemoryBarrierWithGroupSync", - "distance", "dot", "dst", "errorf", "EvaluateAttributeAtCentroid", "EvaluateAttributeAtSample", "EvaluateAttributeSnapped", "exp", "exp2", - "f16tof32", "f32tof16", "faceforward", "firstbithigh", "firstbitlow", "floor", "fma", "fmod", "frac", "frexp", "fwidth", "GetRenderTargetSampleCount", - "GetRenderTargetSamplePosition", "GroupMemoryBarrier", "GroupMemoryBarrierWithGroupSync", "InterlockedAdd", "InterlockedAnd", "InterlockedCompareExchange", - "InterlockedCompareStore", "InterlockedExchange", "InterlockedMax", "InterlockedMin", "InterlockedOr", "InterlockedXor", "isfinite", "isinf", "isnan", - "ldexp", "length", "lerp", "lit", "log", "log10", "log2", "mad", "max", "min", "modf", "msad4", "mul", "noise", "normalize", "pow", "printf", - "Process2DQuadTessFactorsAvg", "Process2DQuadTessFactorsMax", "Process2DQuadTessFactorsMin", "ProcessIsolineTessFactors", "ProcessQuadTessFactorsAvg", - "ProcessQuadTessFactorsMax", "ProcessQuadTessFactorsMin", "ProcessTriTessFactorsAvg", "ProcessTriTessFactorsMax", "ProcessTriTessFactorsMin", - "radians", "rcp", "reflect", "refract", "reversebits", "round", "rsqrt", "saturate", "sign", "sin", "sincos", "sinh", "smoothstep", "sqrt", "step", - "tan", "tanh", "tex1D", "tex1D", "tex1Dbias", "tex1Dgrad", "tex1Dlod", "tex1Dproj", "tex2D", "tex2D", "tex2Dbias", "tex2Dgrad", "tex2Dlod", "tex2Dproj", - "tex3D", "tex3D", "tex3Dbias", "tex3Dgrad", "tex3Dlod", "tex3Dproj", "texCUBE", "texCUBE", "texCUBEbias", "texCUBEgrad", "texCUBElod", "texCUBEproj", "transpose", "trunc" - }; - for (auto& k : identifiers) - { - Identifier id; - id.mDeclaration = "Built-in function"; - langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); - } + static const char *const identifiers[] = { + "abort", "abs", "acos", "all", "AllMemoryBarrier", "AllMemoryBarrierWithGroupSync", "any", "asdouble", "asfloat", "asin", "asint", "asint", "asuint", "asuint", "atan", "atan2", "ceil", "CheckAccessFullyMapped", "clamp", "clip", "cos", "cosh", "countbits", "cross", "D3DCOLORtoUBYTE4", "ddx", "ddx_coarse", "ddx_fine", "ddy", "ddy_coarse", "ddy_fine", "degrees", "determinant", "DeviceMemoryBarrier", "DeviceMemoryBarrierWithGroupSync", "distance", "dot", "dst", "errorf", "EvaluateAttributeAtCentroid", "EvaluateAttributeAtSample", "EvaluateAttributeSnapped", "exp", "exp2", "f16tof32", "f32tof16", "faceforward", "firstbithigh", "firstbitlow", "floor", "fma", "fmod", "frac", "frexp", "fwidth", "GetRenderTargetSampleCount", "GetRenderTargetSamplePosition", "GroupMemoryBarrier", "GroupMemoryBarrierWithGroupSync", "InterlockedAdd", "InterlockedAnd", "InterlockedCompareExchange", "InterlockedCompareStore", "InterlockedExchange", "InterlockedMax", "InterlockedMin", "InterlockedOr", "InterlockedXor", "isfinite", "isinf", "isnan", "ldexp", "length", "lerp", "lit", "log", "log10", "log2", "mad", "max", "min", "modf", "msad4", "mul", "noise", "normalize", "pow", "printf", "Process2DQuadTessFactorsAvg", "Process2DQuadTessFactorsMax", "Process2DQuadTessFactorsMin", "ProcessIsolineTessFactors", "ProcessQuadTessFactorsAvg", "ProcessQuadTessFactorsMax", "ProcessQuadTessFactorsMin", "ProcessTriTessFactorsAvg", "ProcessTriTessFactorsMax", "ProcessTriTessFactorsMin", "radians", "rcp", "reflect", "refract", "reversebits", "round", "rsqrt", "saturate", "sign", "sin", "sincos", "sinh", "smoothstep", "sqrt", "step", "tan", "tanh", "tex1D", "tex1D", "tex1Dbias", "tex1Dgrad", "tex1Dlod", "tex1Dproj", "tex2D", "tex2D", "tex2Dbias", "tex2Dgrad", "tex2Dlod", "tex2Dproj", "tex3D", "tex3D", "tex3Dbias", "tex3Dgrad", "tex3Dlod", "tex3Dproj", "texCUBE", "texCUBE", "texCUBEbias", "texCUBEgrad", "texCUBElod", "texCUBEproj", "transpose", "trunc" + }; + for (auto &k : identifiers) { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } - langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); - langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); + langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - langDef.mSingleLineComment = "//"; + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; - langDef.mCaseSensitive = true; - langDef.mAutoIndentation = true; + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = true; - langDef.mName = "HLSL"; + langDef.mName = "HLSL"; - inited = true; - } - return langDef; + inited = true; + } + return langDef; } -const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL() -{ - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) - { - static const char* const keywords[] = { - "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short", - "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", - "_Noreturn", "_Static_assert", "_Thread_local" - }; - for (auto& k : keywords) - langDef.mKeywords.insert(k); +const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::GLSL() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const keywords[] = { + "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local" + }; + for (auto &k : keywords) + langDef.mKeywords.insert(k); - static const char* const identifiers[] = { - "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", - "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper" - }; - for (auto& k : identifiers) - { - Identifier id; - id.mDeclaration = "Built-in function"; - langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); - } + static const char *const identifiers[] = { + "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper" + }; + for (auto &k : identifiers) { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } - langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); - langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); + langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - langDef.mSingleLineComment = "//"; + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; - langDef.mCaseSensitive = true; - langDef.mAutoIndentation = true; + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = true; - langDef.mName = "GLSL"; + langDef.mName = "GLSL"; - inited = true; - } - return langDef; + inited = true; + } + return langDef; } -const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::C() -{ - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) - { - static const char* const keywords[] = { - "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short", - "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", - "_Noreturn", "_Static_assert", "_Thread_local" - }; - for (auto& k : keywords) - langDef.mKeywords.insert(k); +const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::C() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const keywords[] = { + "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local" + }; + for (auto &k : keywords) + langDef.mKeywords.insert(k); - static const char* const identifiers[] = { - "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", - "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper" - }; - for (auto& k : identifiers) - { - Identifier id; - id.mDeclaration = "Built-in function"; - langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); - } + static const char *const identifiers[] = { + "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper" + }; + for (auto &k : identifiers) { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } - langDef.mTokenize = [](const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end, PaletteIndex & paletteIndex) -> bool - { - paletteIndex = PaletteIndex::Max; + langDef.mTokenize = [](const char *in_begin, const char *in_end, const char *&out_begin, const char *&out_end, PaletteIndex &paletteIndex) -> bool { + paletteIndex = PaletteIndex::Max; - while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin)) - in_begin++; + while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin)) + in_begin++; - if (in_begin == in_end) - { - out_begin = in_end; - out_end = in_end; - paletteIndex = PaletteIndex::Default; - } - else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::String; - else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::CharLiteral; - else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::Identifier; - else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::Number; - else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::Punctuation; + if (in_begin == in_end) { + out_begin = in_end; + out_end = in_end; + paletteIndex = PaletteIndex::Default; + } else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::String; + else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::CharLiteral; + else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Identifier; + else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Number; + else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Punctuation; - return paletteIndex != PaletteIndex::Max; - }; + return paletteIndex != PaletteIndex::Max; + }; - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - langDef.mSingleLineComment = "//"; + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; - langDef.mCaseSensitive = true; - langDef.mAutoIndentation = true; + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = true; - langDef.mName = "C"; + langDef.mName = "C"; - inited = true; - } - return langDef; + inited = true; + } + return langDef; } -const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::SQL() -{ - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) - { - static const char* const keywords[] = { - "ADD", "EXCEPT", "PERCENT", "ALL", "EXEC", "PLAN", "ALTER", "EXECUTE", "PRECISION", "AND", "EXISTS", "PRIMARY", "ANY", "EXIT", "PRINT", "AS", "FETCH", "PROC", "ASC", "FILE", "PROCEDURE", - "AUTHORIZATION", "FILLFACTOR", "PUBLIC", "BACKUP", "FOR", "RAISERROR", "BEGIN", "FOREIGN", "READ", "BETWEEN", "FREETEXT", "READTEXT", "BREAK", "FREETEXTTABLE", "RECONFIGURE", - "BROWSE", "FROM", "REFERENCES", "BULK", "FULL", "REPLICATION", "BY", "FUNCTION", "RESTORE", "CASCADE", "GOTO", "RESTRICT", "CASE", "GRANT", "RETURN", "CHECK", "GROUP", "REVOKE", - "CHECKPOINT", "HAVING", "RIGHT", "CLOSE", "HOLDLOCK", "ROLLBACK", "CLUSTERED", "IDENTITY", "ROWCOUNT", "COALESCE", "IDENTITY_INSERT", "ROWGUIDCOL", "COLLATE", "IDENTITYCOL", "RULE", - "COLUMN", "IF", "SAVE", "COMMIT", "IN", "SCHEMA", "COMPUTE", "INDEX", "SELECT", "CONSTRAINT", "INNER", "SESSION_USER", "CONTAINS", "INSERT", "SET", "CONTAINSTABLE", "INTERSECT", "SETUSER", - "CONTINUE", "INTO", "SHUTDOWN", "CONVERT", "IS", "SOME", "CREATE", "JOIN", "STATISTICS", "CROSS", "KEY", "SYSTEM_USER", "CURRENT", "KILL", "TABLE", "CURRENT_DATE", "LEFT", "TEXTSIZE", - "CURRENT_TIME", "LIKE", "THEN", "CURRENT_TIMESTAMP", "LINENO", "TO", "CURRENT_USER", "LOAD", "TOP", "CURSOR", "NATIONAL", "TRAN", "DATABASE", "NOCHECK", "TRANSACTION", - "DBCC", "NONCLUSTERED", "TRIGGER", "DEALLOCATE", "NOT", "TRUNCATE", "DECLARE", "NULL", "TSEQUAL", "DEFAULT", "NULLIF", "UNION", "DELETE", "OF", "UNIQUE", "DENY", "OFF", "UPDATE", - "DESC", "OFFSETS", "UPDATETEXT", "DISK", "ON", "USE", "DISTINCT", "OPEN", "USER", "DISTRIBUTED", "OPENDATASOURCE", "VALUES", "DOUBLE", "OPENQUERY", "VARYING","DROP", "OPENROWSET", "VIEW", - "DUMMY", "OPENXML", "WAITFOR", "DUMP", "OPTION", "WHEN", "ELSE", "OR", "WHERE", "END", "ORDER", "WHILE", "ERRLVL", "OUTER", "WITH", "ESCAPE", "OVER", "WRITETEXT" - }; +const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::SQL() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const keywords[] = { + "ADD", "EXCEPT", "PERCENT", "ALL", "EXEC", "PLAN", "ALTER", "EXECUTE", "PRECISION", "AND", "EXISTS", "PRIMARY", "ANY", "EXIT", "PRINT", "AS", "FETCH", "PROC", "ASC", "FILE", "PROCEDURE", "AUTHORIZATION", "FILLFACTOR", "PUBLIC", "BACKUP", "FOR", "RAISERROR", "BEGIN", "FOREIGN", "READ", "BETWEEN", "FREETEXT", "READTEXT", "BREAK", "FREETEXTTABLE", "RECONFIGURE", "BROWSE", "FROM", "REFERENCES", "BULK", "FULL", "REPLICATION", "BY", "FUNCTION", "RESTORE", "CASCADE", "GOTO", "RESTRICT", "CASE", "GRANT", "RETURN", "CHECK", "GROUP", "REVOKE", "CHECKPOINT", "HAVING", "RIGHT", "CLOSE", "HOLDLOCK", "ROLLBACK", "CLUSTERED", "IDENTITY", "ROWCOUNT", "COALESCE", "IDENTITY_INSERT", "ROWGUIDCOL", "COLLATE", "IDENTITYCOL", "RULE", "COLUMN", "IF", "SAVE", "COMMIT", "IN", "SCHEMA", "COMPUTE", "INDEX", "SELECT", "CONSTRAINT", "INNER", "SESSION_USER", "CONTAINS", "INSERT", "SET", "CONTAINSTABLE", "INTERSECT", "SETUSER", "CONTINUE", "INTO", "SHUTDOWN", "CONVERT", "IS", "SOME", "CREATE", "JOIN", "STATISTICS", "CROSS", "KEY", "SYSTEM_USER", "CURRENT", "KILL", "TABLE", "CURRENT_DATE", "LEFT", "TEXTSIZE", "CURRENT_TIME", "LIKE", "THEN", "CURRENT_TIMESTAMP", "LINENO", "TO", "CURRENT_USER", "LOAD", "TOP", "CURSOR", "NATIONAL", "TRAN", "DATABASE", "NOCHECK", "TRANSACTION", "DBCC", "NONCLUSTERED", "TRIGGER", "DEALLOCATE", "NOT", "TRUNCATE", "DECLARE", "NULL", "TSEQUAL", "DEFAULT", "NULLIF", "UNION", "DELETE", "OF", "UNIQUE", "DENY", "OFF", "UPDATE", "DESC", "OFFSETS", "UPDATETEXT", "DISK", "ON", "USE", "DISTINCT", "OPEN", "USER", "DISTRIBUTED", "OPENDATASOURCE", "VALUES", "DOUBLE", "OPENQUERY", "VARYING", "DROP", "OPENROWSET", "VIEW", "DUMMY", "OPENXML", "WAITFOR", "DUMP", "OPTION", "WHEN", "ELSE", "OR", "WHERE", "END", "ORDER", "WHILE", "ERRLVL", "OUTER", "WITH", "ESCAPE", "OVER", "WRITETEXT" + }; - for (auto& k : keywords) - langDef.mKeywords.insert(k); + for (auto &k : keywords) + langDef.mKeywords.insert(k); - static const char* const identifiers[] = { - "ABS", "ACOS", "ADD_MONTHS", "ASCII", "ASCIISTR", "ASIN", "ATAN", "ATAN2", "AVG", "BFILENAME", "BIN_TO_NUM", "BITAND", "CARDINALITY", "CASE", "CAST", "CEIL", - "CHARTOROWID", "CHR", "COALESCE", "COMPOSE", "CONCAT", "CONVERT", "CORR", "COS", "COSH", "COUNT", "COVAR_POP", "COVAR_SAMP", "CUME_DIST", "CURRENT_DATE", - "CURRENT_TIMESTAMP", "DBTIMEZONE", "DECODE", "DECOMPOSE", "DENSE_RANK", "DUMP", "EMPTY_BLOB", "EMPTY_CLOB", "EXP", "EXTRACT", "FIRST_VALUE", "FLOOR", "FROM_TZ", "GREATEST", - "GROUP_ID", "HEXTORAW", "INITCAP", "INSTR", "INSTR2", "INSTR4", "INSTRB", "INSTRC", "LAG", "LAST_DAY", "LAST_VALUE", "LEAD", "LEAST", "LENGTH", "LENGTH2", "LENGTH4", - "LENGTHB", "LENGTHC", "LISTAGG", "LN", "LNNVL", "LOCALTIMESTAMP", "LOG", "LOWER", "LPAD", "LTRIM", "MAX", "MEDIAN", "MIN", "MOD", "MONTHS_BETWEEN", "NANVL", "NCHR", - "NEW_TIME", "NEXT_DAY", "NTH_VALUE", "NULLIF", "NUMTODSINTERVAL", "NUMTOYMINTERVAL", "NVL", "NVL2", "POWER", "RANK", "RAWTOHEX", "REGEXP_COUNT", "REGEXP_INSTR", - "REGEXP_REPLACE", "REGEXP_SUBSTR", "REMAINDER", "REPLACE", "ROUND", "ROWNUM", "RPAD", "RTRIM", "SESSIONTIMEZONE", "SIGN", "SIN", "SINH", - "SOUNDEX", "SQRT", "STDDEV", "SUBSTR", "SUM", "SYS_CONTEXT", "SYSDATE", "SYSTIMESTAMP", "TAN", "TANH", "TO_CHAR", "TO_CLOB", "TO_DATE", "TO_DSINTERVAL", "TO_LOB", - "TO_MULTI_BYTE", "TO_NCLOB", "TO_NUMBER", "TO_SINGLE_BYTE", "TO_TIMESTAMP", "TO_TIMESTAMP_TZ", "TO_YMINTERVAL", "TRANSLATE", "TRIM", "TRUNC", "TZ_OFFSET", "UID", "UPPER", - "USER", "USERENV", "VAR_POP", "VAR_SAMP", "VARIANCE", "VSIZE " - }; - for (auto& k : identifiers) - { - Identifier id; - id.mDeclaration = "Built-in function"; - langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); - } + static const char *const identifiers[] = { + "ABS", "ACOS", "ADD_MONTHS", "ASCII", "ASCIISTR", "ASIN", "ATAN", "ATAN2", "AVG", "BFILENAME", "BIN_TO_NUM", "BITAND", "CARDINALITY", "CASE", "CAST", "CEIL", "CHARTOROWID", "CHR", "COALESCE", "COMPOSE", "CONCAT", "CONVERT", "CORR", "COS", "COSH", "COUNT", "COVAR_POP", "COVAR_SAMP", "CUME_DIST", "CURRENT_DATE", "CURRENT_TIMESTAMP", "DBTIMEZONE", "DECODE", "DECOMPOSE", "DENSE_RANK", "DUMP", "EMPTY_BLOB", "EMPTY_CLOB", "EXP", "EXTRACT", "FIRST_VALUE", "FLOOR", "FROM_TZ", "GREATEST", "GROUP_ID", "HEXTORAW", "INITCAP", "INSTR", "INSTR2", "INSTR4", "INSTRB", "INSTRC", "LAG", "LAST_DAY", "LAST_VALUE", "LEAD", "LEAST", "LENGTH", "LENGTH2", "LENGTH4", "LENGTHB", "LENGTHC", "LISTAGG", "LN", "LNNVL", "LOCALTIMESTAMP", "LOG", "LOWER", "LPAD", "LTRIM", "MAX", "MEDIAN", "MIN", "MOD", "MONTHS_BETWEEN", "NANVL", "NCHR", "NEW_TIME", "NEXT_DAY", "NTH_VALUE", "NULLIF", "NUMTODSINTERVAL", "NUMTOYMINTERVAL", "NVL", "NVL2", "POWER", "RANK", "RAWTOHEX", "REGEXP_COUNT", "REGEXP_INSTR", "REGEXP_REPLACE", "REGEXP_SUBSTR", "REMAINDER", "REPLACE", "ROUND", "ROWNUM", "RPAD", "RTRIM", "SESSIONTIMEZONE", "SIGN", "SIN", "SINH", "SOUNDEX", "SQRT", "STDDEV", "SUBSTR", "SUM", "SYS_CONTEXT", "SYSDATE", "SYSTIMESTAMP", "TAN", "TANH", "TO_CHAR", "TO_CLOB", "TO_DATE", "TO_DSINTERVAL", "TO_LOB", "TO_MULTI_BYTE", "TO_NCLOB", "TO_NUMBER", "TO_SINGLE_BYTE", "TO_TIMESTAMP", "TO_TIMESTAMP_TZ", "TO_YMINTERVAL", "TRANSLATE", "TRIM", "TRUNC", "TZ_OFFSET", "UID", "UPPER", "USER", "USERENV", "VAR_POP", "VAR_SAMP", "VARIANCE", "VSIZE " + }; + for (auto &k : identifiers) { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } - langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\\'[^\\\']*\\\'", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); + langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("\\\'[^\\\']*\\\'", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - langDef.mSingleLineComment = "//"; + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; - langDef.mCaseSensitive = false; - langDef.mAutoIndentation = false; + langDef.mCaseSensitive = false; + langDef.mAutoIndentation = false; - langDef.mName = "SQL"; + langDef.mName = "SQL"; - inited = true; - } - return langDef; + inited = true; + } + return langDef; } -const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::AngelScript() -{ - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) - { - static const char* const keywords[] = { - "and", "abstract", "auto", "bool", "break", "case", "cast", "class", "const", "continue", "default", "do", "double", "else", "enum", "false", "final", "float", "for", - "from", "funcdef", "function", "get", "if", "import", "in", "inout", "int", "interface", "int8", "int16", "int32", "int64", "is", "mixin", "namespace", "not", - "null", "or", "out", "override", "private", "protected", "return", "set", "shared", "super", "switch", "this ", "true", "typedef", "uint", "uint8", "uint16", "uint32", - "uint64", "void", "while", "xor" - }; +const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::AngelScript() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const keywords[] = { + "and", "abstract", "auto", "bool", "break", "case", "cast", "class", "const", "continue", "default", "do", "double", "else", "enum", "false", "final", "float", "for", "from", "funcdef", "function", "get", "if", "import", "in", "inout", "int", "interface", "int8", "int16", "int32", "int64", "is", "mixin", "namespace", "not", "null", "or", "out", "override", "private", "protected", "return", "set", "shared", "super", "switch", "this ", "true", "typedef", "uint", "uint8", "uint16", "uint32", "uint64", "void", "while", "xor" + }; - for (auto& k : keywords) - langDef.mKeywords.insert(k); + for (auto &k : keywords) + langDef.mKeywords.insert(k); - static const char* const identifiers[] = { - "cos", "sin", "tab", "acos", "asin", "atan", "atan2", "cosh", "sinh", "tanh", "log", "log10", "pow", "sqrt", "abs", "ceil", "floor", "fraction", "closeTo", "fpFromIEEE", "fpToIEEE", - "complex", "opEquals", "opAddAssign", "opSubAssign", "opMulAssign", "opDivAssign", "opAdd", "opSub", "opMul", "opDiv" - }; - for (auto& k : identifiers) - { - Identifier id; - id.mDeclaration = "Built-in function"; - langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); - } + static const char *const identifiers[] = { + "cos", "sin", "tab", "acos", "asin", "atan", "atan2", "cosh", "sinh", "tanh", "log", "log10", "pow", "sqrt", "abs", "ceil", "floor", "fraction", "closeTo", "fpFromIEEE", "fpToIEEE", "complex", "opEquals", "opAddAssign", "opSubAssign", "opMulAssign", "opDivAssign", "opAdd", "opSub", "opMul", "opDiv" + }; + for (auto &k : identifiers) { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } - langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); + langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - langDef.mSingleLineComment = "//"; + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; - langDef.mCaseSensitive = true; - langDef.mAutoIndentation = true; + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = true; - langDef.mName = "AngelScript"; + langDef.mName = "AngelScript"; - inited = true; - } - return langDef; + inited = true; + } + return langDef; } -const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::Lua() -{ - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) - { - static const char* const keywords[] = { - "and", "break", "do", "", "else", "elseif", "end", "false", "for", "function", "if", "in", "", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while" - }; +const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::Lua() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const keywords[] = { + "and", "break", "do", "", "else", "elseif", "end", "false", "for", "function", "if", "in", "", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while" + }; - for (auto& k : keywords) - langDef.mKeywords.insert(k); + for (auto &k : keywords) + langDef.mKeywords.insert(k); - static const char* const identifiers[] = { - "assert", "collectgarbage", "dofile", "error", "getmetatable", "ipairs", "loadfile", "load", "loadstring", "next", "pairs", "pcall", "print", "rawequal", "rawlen", "rawget", "rawset", - "select", "setmetatable", "tonumber", "tostring", "type", "xpcall", "_G", "_VERSION","arshift", "band", "bnot", "bor", "bxor", "btest", "extract", "lrotate", "lshift", "replace", - "rrotate", "rshift", "create", "resume", "running", "status", "wrap", "yield", "isyieldable", "debug","getuservalue", "gethook", "getinfo", "getlocal", "getregistry", "getmetatable", - "getupvalue", "upvaluejoin", "upvalueid", "setuservalue", "sethook", "setlocal", "setmetatable", "setupvalue", "traceback", "close", "flush", "input", "lines", "open", "output", "popen", - "read", "tmpfile", "type", "write", "close", "flush", "lines", "read", "seek", "setvbuf", "write", "__gc", "__tostring", "abs", "acos", "asin", "atan", "ceil", "cos", "deg", "exp", "tointeger", - "floor", "fmod", "ult", "log", "max", "min", "modf", "rad", "random", "randomseed", "sin", "sqrt", "string", "tan", "type", "atan2", "cosh", "sinh", "tanh", - "pow", "frexp", "ldexp", "log10", "pi", "huge", "maxinteger", "mininteger", "loadlib", "searchpath", "seeall", "preload", "cpath", "path", "searchers", "loaded", "module", "require", "clock", - "date", "difftime", "execute", "exit", "getenv", "remove", "rename", "setlocale", "time", "tmpname", "byte", "char", "dump", "find", "format", "gmatch", "gsub", "len", "lower", "match", "rep", - "reverse", "sub", "upper", "pack", "packsize", "unpack", "concat", "maxn", "insert", "pack", "unpack", "remove", "move", "sort", "offset", "codepoint", "char", "len", "codes", "charpattern", - "coroutine", "table", "io", "os", "string", "utf8", "bit32", "math", "debug", "package" - }; - for (auto& k : identifiers) - { - Identifier id; - id.mDeclaration = "Built-in function"; - langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); - } + static const char *const identifiers[] = { + "assert", "collectgarbage", "dofile", "error", "getmetatable", "ipairs", "loadfile", "load", "loadstring", "next", "pairs", "pcall", "print", "rawequal", "rawlen", "rawget", "rawset", "select", "setmetatable", "tonumber", "tostring", "type", "xpcall", "_G", "_VERSION", "arshift", "band", "bnot", "bor", "bxor", "btest", "extract", "lrotate", "lshift", "replace", "rrotate", "rshift", "create", "resume", "running", "status", "wrap", "yield", "isyieldable", "debug", "getuservalue", "gethook", "getinfo", "getlocal", "getregistry", "getmetatable", "getupvalue", "upvaluejoin", "upvalueid", "setuservalue", "sethook", "setlocal", "setmetatable", "setupvalue", "traceback", "close", "flush", "input", "lines", "open", "output", "popen", "read", "tmpfile", "type", "write", "close", "flush", "lines", "read", "seek", "setvbuf", "write", "__gc", "__tostring", "abs", "acos", "asin", "atan", "ceil", "cos", "deg", "exp", "tointeger", "floor", "fmod", "ult", "log", "max", "min", "modf", "rad", "random", "randomseed", "sin", "sqrt", "string", "tan", "type", "atan2", "cosh", "sinh", "tanh", "pow", "frexp", "ldexp", "log10", "pi", "huge", "maxinteger", "mininteger", "loadlib", "searchpath", "seeall", "preload", "cpath", "path", "searchers", "loaded", "module", "require", "clock", "date", "difftime", "execute", "exit", "getenv", "remove", "rename", "setlocale", "time", "tmpname", "byte", "char", "dump", "find", "format", "gmatch", "gsub", "len", "lower", "match", "rep", "reverse", "sub", "upper", "pack", "packsize", "unpack", "concat", "maxn", "insert", "pack", "unpack", "remove", "move", "sort", "offset", "codepoint", "char", "len", "codes", "charpattern", "coroutine", "table", "io", "os", "string", "utf8", "bit32", "math", "debug", "package" + }; + for (auto &k : identifiers) { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } - langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\\'[^\\\']*\\\'", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); + langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("\\\'[^\\\']*\\\'", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); - langDef.mCommentStart = "--[["; - langDef.mCommentEnd = "]]"; - langDef.mSingleLineComment = "--"; + langDef.mCommentStart = "--[["; + langDef.mCommentEnd = "]]"; + langDef.mSingleLineComment = "--"; - langDef.mCaseSensitive = true; - langDef.mAutoIndentation = false; + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = false; - langDef.mName = "Lua"; + langDef.mName = "Lua"; - inited = true; - } - return langDef; + inited = true; + } + return langDef; } diff --git a/lib/libimhex/source/pattern_language/lexer.cpp b/lib/libimhex/source/pattern_language/lexer.cpp index 82d67da29..8b7f09b91 100644 --- a/lib/libimhex/source/pattern_language/lexer.cpp +++ b/lib/libimhex/source/pattern_language/lexer.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -24,116 +25,103 @@ namespace hex::pl { return ret; } - size_t getIntegerLiteralLength(const std::string &string) { - return string.find_first_not_of("0123456789ABCDEFabcdef.xUL"); - } - bool isIdentifierCharacter(char c) { return std::isalnum(c) || c == '_'; } - std::optional parseIntegerLiteral(const std::string &string) { - Token::ValueType type = Token::ValueType::Any; - Token::Literal result; + size_t getIntegerLiteralLength(std::string_view string) { + auto count = string.find_first_not_of("0123456789ABCDEFabcdef'xXoOpP.uU"); + if (count == std::string_view::npos) + return string.size(); + else + return count; + } - u8 base; + std::optional lexIntegerLiteral(std::string_view string) { - auto endPos = getIntegerLiteralLength(string); - auto numberData = std::string_view(string).substr(0, endPos); - - if (numberData.ends_with('U')) { - type = Token::ValueType::Unsigned128Bit; - numberData.remove_suffix(1); - } else if (!numberData.starts_with("0x") && !numberData.starts_with("0b")) { - if (numberData.ends_with('F')) { - type = Token::ValueType::Float; - numberData.remove_suffix(1); - } else if (numberData.ends_with('D')) { - type = Token::ValueType::Double; - numberData.remove_suffix(1); - } - } - - if (numberData.starts_with("0x")) { - numberData = numberData.substr(2); - base = 16; - - if (Token::isFloatingPoint(type)) - return std::nullopt; - - if (numberData.find_first_not_of("0123456789ABCDEFabcdef") != std::string_view::npos) - return std::nullopt; - } else if (numberData.starts_with("0b")) { - numberData = numberData.substr(2); - base = 2; - - if (Token::isFloatingPoint(type)) - return std::nullopt; - - if (numberData.find_first_not_of("01") != std::string_view::npos) - return std::nullopt; - } else if (numberData.find('.') != std::string_view::npos || Token::isFloatingPoint(type)) { - base = 10; - if (type == Token::ValueType::Any) - type = Token::ValueType::Double; - - if (std::count(numberData.begin(), numberData.end(), '.') > 1 || numberData.find_first_not_of("0123456789.") != std::string_view::npos) - return std::nullopt; - - if (numberData.ends_with('.')) - return std::nullopt; - } else if (isdigit(numberData[0])) { - base = 10; - - if (numberData.find_first_not_of("0123456789") != std::string_view::npos) - return std::nullopt; - } else return std::nullopt; - - if (type == Token::ValueType::Any) - type = Token::ValueType::Signed128Bit; - - - if (numberData.length() == 0) - return std::nullopt; - - if (Token::isUnsigned(type) || Token::isSigned(type)) { - u128 integer = 0; - for (const char &c : numberData) { - integer *= base; - - if (isdigit(c)) - integer += (c - '0'); - else if (c >= 'A' && c <= 'F') - integer += 10 + (c - 'A'); - else if (c >= 'a' && c <= 'f') - integer += 10 + (c - 'a'); - else return std::nullopt; + if (std::count(string.begin(), string.end(), '.') == 1) { + // Parse double + char suffix = 0x00; + if (string.ends_with('D') || string.ends_with('F') || string.ends_with('d') || string.ends_with('f')) { + suffix = string.back(); + string = string.substr(0, string.length() - 1); } - switch (type) { - case Token::ValueType::Unsigned128Bit: - return { u128(integer) }; - case Token::ValueType::Signed128Bit: - return { i128(integer) }; - default: + char *end = nullptr; + double value = std::strtod(string.begin(), &end); + + if (end == string.end()) { + switch (suffix) { + case 'd': + case 'D': + return double(value); + case 'f': + case 'F': + return float(value); + default: + return value; + } + } + } else { + bool unsignedNumber = false; + if (string.ends_with('U') || string.ends_with('u')) { + unsignedNumber = true; + string = string.substr(0, string.length() - 1); + } + + i128 value; + if (string.starts_with("0x") || string.starts_with("0X")) { + // Parse hexadecimal + + auto [p, error] = std::from_chars(string.begin() + 2, string.end(), value, 16); + + if (error == std::errc::invalid_argument || p != string.end()) + return std::nullopt; + } else if (string.starts_with("0o") || string.starts_with("0O")) { + // Parse octal + + auto [p, error] = std::from_chars(string.begin() + 2, string.end(), value, 8); + + if (error == std::errc::invalid_argument || p != string.end()) + return std::nullopt; + } else if (string.starts_with("0b") || string.starts_with("0B")) { + // Parse binary + + auto [p, error] = std::from_chars(string.begin() + 2, string.end(), value, 2); + + if (error == std::errc::invalid_argument || p != string.end()) + return std::nullopt; + } else { + // Parse decimal + + auto [p, error] = std::from_chars(string.begin(), string.end(), value, 10); + + if (error == std::errc::invalid_argument || p != string.end()) return std::nullopt; } - } else if (Token::isFloatingPoint(type)) { - double floatingPoint = strtod(numberData.data(), nullptr); - switch (type) { - case Token::ValueType::Float: - return { float(floatingPoint) }; - case Token::ValueType::Double: - return { double(floatingPoint) }; - default: - return std::nullopt; - } + if (unsignedNumber) + return u128(value); + else + return i128(value); } return std::nullopt; } + std::optional lexIntegerLiteralWithSeparator(std::string_view string) { + + if (string.starts_with('\'') || string.ends_with('\'')) + return std::nullopt; + else if (string.find('\'') == std::string_view::npos) + return lexIntegerLiteral(string); + else { + auto preprocessedString = std::string(string); + preprocessedString.erase(std::remove(preprocessedString.begin(), preprocessedString.end(), '\''), preprocessedString.end()); + return lexIntegerLiteral(preprocessedString); + } + } + std::optional> getCharacter(const std::string &string) { if (string.length() < 1) @@ -517,14 +505,15 @@ namespace hex::pl { offset += identifier.length(); } else if (std::isdigit(c)) { - auto integer = parseIntegerLiteral(&code[offset]); + auto integerLength = getIntegerLiteralLength(&code[offset]); + auto integer = lexIntegerLiteralWithSeparator(std::string_view(&code[offset], integerLength)); if (!integer.has_value()) throwLexerError("invalid integer literal", lineNumber); tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(integer.value()))); - offset += getIntegerLiteralLength(&code[offset]); + offset += integerLength; } else throwLexerError("unknown token", lineNumber); } diff --git a/main/source/init/tasks.cpp b/main/source/init/tasks.cpp index 92721503c..a7a9e7e4e 100644 --- a/main/source/init/tasks.cpp +++ b/main/source/init/tasks.cpp @@ -192,7 +192,9 @@ namespace hex::init { ContentRegistry::Interface::getToolbarItems().clear(); ContentRegistry::Interface::getMainMenuItems().clear(); ContentRegistry::Interface::getMenuItems().clear(); + ContentRegistry::Interface::getSidebarItems().clear(); ContentRegistry::Interface::getTitleBarButtons().clear(); + ContentRegistry::Interface::getLayouts().clear(); ShortcutManager::clearShortcuts(); diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 5b9badb9c..de0555203 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -30,7 +30,7 @@ namespace hex::plugin::builtin { for (const auto name : builtInTypes) { TextEditor::Identifier id; - id.mDeclaration = "Built-in type"; + id.mDeclaration = ""; langDef.mIdentifiers.insert(std::make_pair(std::string(name), id)); }