diff --git a/lib/external/libwolv b/lib/external/libwolv index 3c7965a3f..0181fd6ff 160000 --- a/lib/external/libwolv +++ b/lib/external/libwolv @@ -1 +1 @@ -Subproject commit 3c7965a3fdf4441392c1609b1719a020cd29362b +Subproject commit 0181fd6ffd4e078177eebb12f2e9f77b9bec515e diff --git a/lib/external/pattern_language b/lib/external/pattern_language index 9954c25d9..7cbc1ac7f 160000 --- a/lib/external/pattern_language +++ b/lib/external/pattern_language @@ -1 +1 @@ -Subproject commit 9954c25d9c180823ce89051b1d97c892176c46fa +Subproject commit 7cbc1ac7f860ad52a99359770c9f224a05d9a3ad diff --git a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h index d0c16c544..35d2f43a9 100644 --- a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h +++ b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h @@ -235,6 +235,7 @@ public: bool mGlobalDocComment : 1; bool mDeactivated : 1; bool mPreprocessor : 1; + bool mMatchedBracket : 1; }; union Flags { Flags(char value) : mValue(value) {} @@ -244,6 +245,8 @@ public: }; constexpr static char InComment = 31; + int GetCharacterColumn(int aIndex) const; + class LineIterator { public: strConstIter mCharsIter; @@ -817,6 +820,7 @@ public: void MoveBottom(bool aSelect = false); void MoveHome(bool aSelect = false); void MoveEnd(bool aSelect = false); + void MoveToMatchedBracket(bool aSelect = false); void SetSelectionStart(const Coordinates& aPosition); void SetSelectionEnd(const Coordinates& aPosition); @@ -961,6 +965,25 @@ private: typedef std::vector UndoBuffer; + struct MatchedBracket { + bool mActive=false; + bool mChanged=false; + Coordinates mNearCursor = {}; + Coordinates mMatched = {}; + static const std::string mSeparators; + static const std::string mOperators; + MatchedBracket(const MatchedBracket &other) : mActive(other.mActive), mChanged(other.mChanged), mNearCursor(other.mNearCursor), mMatched(other.mMatched) {} + MatchedBracket() : mActive(false), mChanged(false), mNearCursor(0,0), mMatched(0,0) {} + MatchedBracket(bool active, bool changed, const Coordinates &nearCursor, const Coordinates &matched) : mActive(active), mChanged(changed), mNearCursor(nearCursor), mMatched(matched) {} + bool CheckPosition(TextEditor *editor, const Coordinates &aFrom); + bool IsNearABracket(TextEditor *editor, const Coordinates &aFrom); + int DetectDirection(TextEditor *editor, const Coordinates &aFrom); + + void FindMatchingBracket(TextEditor *editor); + bool IsActive() const { return mActive; } + bool hasChanged() const { return mChanged; } + }; + void ProcessInputs(); void ColorizeRange(); void ColorizeInternal(); @@ -980,11 +1003,11 @@ private: Coordinates FindNextWord(const Coordinates& aFrom) const; Coordinates StringIndexToCoordinates(int aIndex, const std::string &str) const; int GetCharacterIndex(const Coordinates& aCoordinates) const; - int GetCharacterColumn(int aLine, int aIndex) const; + Coordinates GetCharacterCoordinates(int aLine, int aIndex) const; int GetLineCharacterCount(int aLine) const; - int Utf8CharsToBytes(const Coordinates &aCoordinates) const; - static int Utf8CharsToBytes(std::string line, uint32_t start, uint32_t numChars); - unsigned long long GetLineByteCount(int aLine) const; + int Utf8CharsToBytes(const Coordinates &aCoordinates) const; + static int Utf8CharsToBytes(std::string line, uint32_t start, uint32_t numChars); + unsigned long long GetLineByteCount(int aLine) const; int GetStringCharacterCount(std::string str) const; int GetLineMaxColumn(int aLine) const; bool IsOnWordBoundary(const Coordinates& aAt) const; @@ -1038,6 +1061,7 @@ private: bool mIgnoreImGuiChild = false; bool mShowWhitespaces = true; + MatchedBracket mMatchedBracket={}; Palette mPalette = {}; LanguageDefinition mLanguageDefinition = {}; RegexList mRegexList; diff --git a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp index 4ab7807c0..3917186da 100644 --- a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp +++ b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp @@ -29,6 +29,9 @@ const int TextEditor::sCursorBlinkOnTime = 800; TextEditor::Palette sPaletteBase = TextEditor::GetDarkPalette(); TextEditor::FindReplaceHandler::FindReplaceHandler() : mWholeWord(false),mFindRegEx(false),mMatchCase(false) {} +const std::string TextEditor::MatchedBracket::mSeparators = "()[]{}"; +const std::string TextEditor::MatchedBracket::mOperators = "<>"; + TextEditor::TextEditor() { mStartTime = ImGui::GetTime() * 1000; @@ -131,13 +134,16 @@ TextEditor::Coordinates TextEditor::GetActualCursorCoordinates() const { TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates &aValue) const { Coordinates result = aValue; - if (aValue.mLine < 0) - result.mLine = mLines.size() + aValue.mLine; - if (aValue.mColumn < 0) - result.mColumn = GetLineMaxColumn(result.mLine) + aValue.mColumn + 1; + auto lineCount = (int)mLines.size(); + if (aValue.mLine < 0 && lineCount + aValue.mLine >= 0) + result.mLine = lineCount + aValue.mLine; - result.mLine = std::clamp(result.mLine, 0, (int)mLines.size()-1); - result.mColumn = std::clamp(result.mColumn, 0, GetLineMaxColumn(result.mLine)); + auto maxColumn = GetLineMaxColumn(result.mLine) + 1; + if (aValue.mColumn < 0 && maxColumn + aValue.mColumn >= 0) + result.mColumn = maxColumn + aValue.mColumn; + + result.mLine = std::clamp(result.mLine, 0, lineCount - 1); + result.mColumn = std::clamp(result.mColumn, 0, maxColumn - 1); return result; } @@ -196,7 +202,7 @@ void TextEditor::Advance(Coordinates &aCoordinates) const { auto characterIndex = GetCharacterIndex(aCoordinates); int maxDelta = line.size() - characterIndex; characterIndex += std::min(UTF8CharLength(line[characterIndex]), maxDelta); - aCoordinates.mColumn = GetCharacterColumn(aCoordinates.mLine, characterIndex); + aCoordinates.mColumn = line.GetCharacterColumn(characterIndex); } void TextEditor::DeleteRange(const Coordinates &aStart, const Coordinates &aEnd) { @@ -338,7 +344,7 @@ TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates &aFrom) cons while (cindex > 0 && isspace(line.mChars[cindex-1])) --cindex; } - return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex)); + return GetCharacterCoordinates(at.mLine, cindex); } TextEditor::Coordinates TextEditor::FindWordEnd(const Coordinates &aFrom) const { @@ -359,7 +365,7 @@ TextEditor::Coordinates TextEditor::FindWordEnd(const Coordinates &aFrom) const while (cindex < (int)line.mChars.size() && isspace(line.mChars[cindex])) ++cindex; } - return Coordinates(aFrom.mLine, GetCharacterColumn(aFrom.mLine, cindex)); + return GetCharacterCoordinates(at.mLine, cindex); } TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates &aFrom) const { @@ -381,7 +387,7 @@ TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates &aFrom) const while (cindex < (int)line.mChars.size() && (ispunct(line.mChars[cindex]))) ++cindex; } - return Coordinates(aFrom.mLine, GetCharacterColumn(aFrom.mLine, cindex)); + return GetCharacterCoordinates(at.mLine, cindex); } TextEditor::Coordinates TextEditor::FindPreviousWord(const Coordinates &aFrom) const { @@ -403,7 +409,7 @@ TextEditor::Coordinates TextEditor::FindPreviousWord(const Coordinates &aFrom) c while (cindex > 0 && ispunct(line.mChars[cindex-1])) --cindex; } - return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex)); + return GetCharacterCoordinates(at.mLine, cindex); } @@ -469,25 +475,24 @@ int TextEditor::GetCharacterIndex(const Coordinates &aCoordinates) const { return index; } -int TextEditor::GetCharacterColumn(int aLine, int aIndex) const { - if (aLine >= mLines.size()) - return 0; - if (aLine < 0) - return 0; - auto &line = mLines[aLine]; +int TextEditor::Line::GetCharacterColumn(int aIndex) const { int col = 0; int i = 0; - while (i < aIndex && i < (int)line.size()) { - auto c = line[i]; + while (i < aIndex && i < (int)size()) { + auto c = mChars[i]; i += UTF8CharLength(c); - if (c == '\t') - col = (col / mTabSize) * mTabSize + mTabSize; - else - col++; + col++; } return col; } +TextEditor::Coordinates TextEditor::GetCharacterCoordinates(int aLine, int aIndex) const { + if (aLine < 0 || aLine >= (int)mLines.size()) + return Coordinates(0, 0); + auto &line = mLines[aLine]; + return Coordinates(aLine, line.GetCharacterColumn(aIndex)); +} + int TextEditor::GetStringCharacterCount(std::string str) const { if (str.empty()) return 0; @@ -843,6 +848,198 @@ void TextEditor::SetFocus() { mUpdateFocus = false; } +bool TextEditor::MatchedBracket::CheckPosition(TextEditor *editor, const Coordinates &aFrom) { + auto lineIndex = aFrom.mLine; + auto line = editor->mLines[lineIndex].mChars; + auto colors = editor->mLines[lineIndex].mColors; + if (!line.empty() && colors.empty()) + return false; + auto result = aFrom.mColumn; + auto character = line[result]; + auto color = colors[result]; + if (mSeparators.find(character) != std::string::npos && (static_cast(color) == PaletteIndex::Separator || static_cast(color) == PaletteIndex::WarningText) || + mOperators.find(character) != std::string::npos && (static_cast(color) == PaletteIndex::Operator || static_cast(color) == PaletteIndex::WarningText)) { + if (mNearCursor != editor->GetCharacterCoordinates(lineIndex, result)) { + mNearCursor = editor->GetCharacterCoordinates(lineIndex, result); + mChanged = true; + } + mActive = true; + return true; + } + return false; +} + +int TextEditor::MatchedBracket::DetectDirection(TextEditor *editor, const Coordinates &aFrom) { + int result = 0; // -1 previous 0 current + auto from = editor->SanitizeCoordinates(aFrom); + auto lineIndex = from.mLine; + auto charIndex = editor->GetCharacterIndex(from); + if (charIndex == 0) // no previous character + return 0; + auto line = editor->mLines[lineIndex].mChars; + auto ch1 = line[charIndex-1]; + auto ch2 = line[charIndex]; + std::string brackets = "()[]{}<>"; + auto idx1 = brackets.find(ch1); + auto idx2 = brackets.find(ch2); + if (idx1 == std::string::npos && idx2 == std::string::npos) // no brackets + return 0; + if (idx1 != std::string::npos && idx2 != std::string::npos) { + if (idx1 % 2) // closing bracket + any bracket + return -1; + else if (!(idx1 % 2) && !(idx2 % 2)) // opening bracket + opening bracket + return 0; + } else if (idx1 != std::string::npos) // only first bracket + return -1; + else if (idx2 != std::string::npos) // only second bracket + return 0; + + return result; +} + +bool TextEditor::MatchedBracket::IsNearABracket(TextEditor *editor, const Coordinates &aFrom) { + auto from = editor->SanitizeCoordinates(aFrom); + auto lineIndex = from.mLine; + auto charIndex = editor->GetCharacterIndex(from); + auto direction1 = DetectDirection(editor, from); + auto direction2 = -(direction1 + 1); + if (CheckPosition(editor, Coordinates(lineIndex, charIndex+direction1))) + return true; + if (CheckPosition(editor, Coordinates(lineIndex, charIndex+direction2))) + return true; + uint64_t result = 0; + std::string line = editor->mLines[lineIndex].mChars; + if (charIndex==0) + if (line[0] == ' ') + result = std::string::npos; + else + result = 0; + else + result = line.find_last_not_of(' ', charIndex-1); + if (result != std::string::npos) { + if (CheckPosition(editor, Coordinates(lineIndex, result))) + return true; + } + result = line.find_first_not_of(' ', charIndex); + if (result != std::string::npos) { + if (CheckPosition(editor, Coordinates(lineIndex, result))) + return true; + } + if (mActive) { + editor->mLines[mNearCursor.mLine].mColorized = false; + editor->mLines[mMatched.mLine].mColorized = false; + mActive = false; + editor->Colorize(); + } + return false; +} + +void TextEditor::MatchedBracket::FindMatchingBracket(TextEditor *editor) { + auto from = editor->SanitizeCoordinates(mNearCursor); + mMatched = from; + auto lineIndex = from.mLine; + auto maxLineIndex = editor->mLines.size() - 1; + auto charIndex = editor->GetCharacterIndex(from); + std::string line = editor->mLines[lineIndex].mChars; + std::string colors = editor->mLines[lineIndex].mColors; + if (!line.empty() && colors.empty()) { + mActive = false; + return; + } + std::string brackets = "()[]{}<>"; + char bracketChar = line[charIndex]; + char color1; + auto idx = brackets.find_first_of(bracketChar); + if (idx == std::string::npos) { + if (mActive) { + mActive = false; + editor->Colorize(); + } + return; + } + auto bracketChar2 = brackets[idx ^ 1]; + brackets = bracketChar; + brackets += bracketChar2; + int32_t direction = 1 - 2 * (idx % 2); + if (idx > 5) + color1 = static_cast(PaletteIndex::Operator); + else + color1 = static_cast(PaletteIndex::Separator); + char color = static_cast(PaletteIndex::WarningText); + int32_t depth = 1; + if (charIndex == (line.size()-1) * (1 + direction) / 2 ) { + if (lineIndex == maxLineIndex * (1 + direction) / 2) { + mActive = false; + return; + } + lineIndex += direction; + line = editor->mLines[lineIndex].mChars; + colors = editor->mLines[lineIndex].mColors; + if (!line.empty() && colors.empty()) { + mActive = false; + return; + } + charIndex = (line.size()-1) * (1 - direction) / 2 - direction; + } + for (int32_t i = charIndex + direction; ; i += direction) { + if (direction == 1) + idx = line.find_first_of(brackets, i); + else + idx = line.find_last_of(brackets, i); + if (idx != std::string::npos) { + if (line[idx] == bracketChar && (colors[idx] == color || colors[idx] == color1)) { + ++depth; + i = idx; + } else if (line[idx] == bracketChar2 && (colors[idx] == color) || colors[idx] == color1) { + --depth; + if (depth == 0) { + if (mMatched != editor->GetCharacterCoordinates(lineIndex, idx)) { + mMatched = editor->GetCharacterCoordinates(lineIndex, idx); + mChanged = true; + } + mActive = true; + break; + } + i = idx; + } else { + i = idx; + } + } else { + if (direction == 1) + i = line.size()-1; + else + i = 0; + } + if ((int32_t)(direction * i) >= (int32_t)((line.size() - 1) * (1 + direction) / 2)) { + if (lineIndex == maxLineIndex * (1 + direction) / 2) { + if (mActive) { + mActive = false; + mChanged = true; + } + break; + } else { + lineIndex += direction; + line = editor->mLines[lineIndex].mChars; + colors = editor->mLines[lineIndex].mColors; + if (!line.empty() && colors.empty()) { + mActive = false; + return; + } + i = (line.size() - 1) * (1 - direction) / 2 - direction; + } + } + } + + if (mChanged) { + editor->mLines[mNearCursor.mLine].mColorized = false; + editor->mLines[mMatched.mLine].mColorized = false; + + editor->Colorize(); + mChanged = false; + } + +} + void TextEditor::RenderText(const char *aTitle, const ImVec2 &lineNumbersStartPos, const ImVec2 &textEditorSize) { /* Compute mCharAdvance regarding scaled font size (Ctrl + mouse wheel)*/ const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; @@ -1001,6 +1198,8 @@ void TextEditor::RenderText(const char *aTitle, const ImVec2 &lineNumbersStartPo drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]); if (elapsed > sCursorBlinkInterval) mStartTime = timeEnd; + if (mMatchedBracket.IsNearABracket(this, mState.mCursorPosition)) + mMatchedBracket.FindMatchingBracket(this); } } } @@ -1349,7 +1548,7 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) { } if (modified) { - start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0)); + start = GetCharacterCoordinates(start.mLine, 0); Coordinates rangeEnd; if (originalEnd.mColumn != 0) { end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine)); @@ -1414,7 +1613,7 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) { newLine.insert(newLine.end(), line.begin() + cstart, line.end()); line.erase(line.begin() + cstart,-1); line.mColorized = false; - SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, cpos))); + SetCursorPosition(GetCharacterCoordinates(coord.mLine + 1, cpos)); u.mAdded = (char)aChar; } else if (aChar == '\t') { auto &line = mLines[coord.mLine]; @@ -1425,7 +1624,7 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) { std::string spaces(spacesToInsert, ' '); line.insert(line.begin() + cindex, spaces.begin(), spaces.end()); line.mColorized = false; - SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + spacesToInsert))); + SetCursorPosition(GetCharacterCoordinates(coord.mLine, cindex + spacesToInsert)); } else { auto spacesToRemove = (cindex % mTabSize); if (spacesToRemove == 0) spacesToRemove = mTabSize; @@ -1437,7 +1636,7 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) { } } line.mColorized = false; - SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, std::max(0, cindex)))); + SetCursorPosition(GetCharacterCoordinates(coord.mLine, std::max(0, cindex))); } } else { @@ -1451,7 +1650,7 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) { auto d = UTF8CharLength(line[cindex]); u.mRemovedStart = mState.mCursorPosition; - u.mRemovedEnd = Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + d)); + u.mRemovedEnd = GetCharacterCoordinates(coord.mLine, cindex + d); u.mRemoved = std::string(line.mChars.begin() + cindex, line.mChars.begin() + cindex + d); line.erase(line.begin() + cindex, d); line.mColorized = false; @@ -1460,7 +1659,7 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) { line.mColorized = false; u.mAdded = buf; auto charCount = GetStringCharacterCount(buf); - SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + charCount))); + SetCursorPosition(GetCharacterCoordinates(coord.mLine, cindex + charCount)); } else return; } @@ -1593,6 +1792,32 @@ void TextEditor::JumpToCoords(const Coordinates &aNewPos) { SetFocusAtCoords(aNewPos); } +void TextEditor::MoveToMatchedBracket(bool aSelect) { + ResetCursorBlinkTime(); + if (mMatchedBracket.IsNearABracket(this, mState.mCursorPosition)) { + mMatchedBracket.FindMatchingBracket(this); + auto oldPos = mMatchedBracket.mNearCursor; + auto newPos = mMatchedBracket.mMatched; + if (newPos != Coordinates(-1, -1)) { + if (aSelect) { + if (oldPos == mInteractiveStart) + mInteractiveStart = newPos; + else if (oldPos == mInteractiveEnd) + mInteractiveEnd = newPos; + else { + mInteractiveStart = newPos; + mInteractiveEnd = oldPos; + } + } else + mInteractiveStart = mInteractiveEnd = newPos; + + SetSelection(mInteractiveStart, mInteractiveEnd); + SetCursorPosition(newPos); + EnsureCursorVisible(); + } + } +} + void TextEditor::MoveUp(int aAmount, bool aSelect) { ResetCursorBlinkTime(); auto oldPos = mState.mCursorPosition; @@ -1696,7 +1921,7 @@ void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) { } } - mState.mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex)); + mState.mCursorPosition = GetCharacterCoordinates(lindex, cindex); IM_ASSERT(mState.mCursorPosition.mColumn >= 0); if (aSelect) { @@ -1728,7 +1953,7 @@ void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) { auto lindex = mState.mCursorPosition.mLine; while (aAmount-- > 0) { - auto &line = mLines[lindex]; + const auto &line = mLines[lindex]; if (cindex >= line.size()) { if (lindex < mLines.size() - 1) { @@ -1749,7 +1974,7 @@ void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) { } } - mState.mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex)); + mState.mCursorPosition = GetCharacterCoordinates(lindex, cindex); IM_ASSERT(mState.mCursorPosition.mColumn >= 0); if (aSelect) { @@ -1989,7 +2214,7 @@ void TextEditor::Backspace() { u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); --u.mRemovedStart.mColumn; - mState.mCursorPosition.mColumn = GetCharacterColumn(mState.mCursorPosition.mLine, cindex); + mState.mCursorPosition.mColumn = line.GetCharacterColumn(cindex); u.mRemoved = GetText(u.mRemovedStart, u.mRemovedEnd); if (cend > cindex && cend < (int) line.size()) line.erase(line.begin() + cindex, cend-cindex); @@ -2019,10 +2244,7 @@ void TextEditor::SelectWordUnderCursor() { } void TextEditor::SelectAll() { - if (isEmpty()) - return; - - SetSelection(Coordinates(0, 0), Coordinates((int)mLines.size(), mLines.empty() ? 0 : GetLineMaxColumn((int)mLines.size() - 1))); + SetSelection(Coordinates(0, 0), Coordinates(-1, -1)); } bool TextEditor::HasSelection() const { @@ -2096,7 +2318,7 @@ std::string TextEditor::ReplaceStrings(std::string string, const std::string &se } std::vector TextEditor::SplitString(const std::string &string, const std::string &delimiter, bool removeEmpty) { - if (delimiter.empty()) { + if (delimiter.empty() || string.empty()) { return { string }; } @@ -2113,7 +2335,8 @@ std::vector TextEditor::SplitString(const std::string &string, cons result.emplace_back(std::move(token)); } - result.emplace_back(string.substr(start)); + if (start < string.length()) + result.emplace_back(string.substr(start)); if (removeEmpty) std::erase_if(result, [](const auto &string) { return string.empty(); }); @@ -2757,7 +2980,14 @@ void TextEditor::ColorizeRange() { token_length = token_end - token_begin; } } - if ((token_color != PaletteIndex::Directive && token_color != PaletteIndex::PreprocIdentifier) || flags.mBits.mDeactivated) { + if (flags.mBits.mMatchedBracket) { + if (token_color != PaletteIndex::WarningText) { + token_color = PaletteIndex::WarningText; + } + token_length = token_end - token_begin; + } else if (flags.mBits.mPreprocessor && !flags.mBits.mDeactivated) { + token_length = token_end - token_begin; + } else if ((token_color != PaletteIndex::Directive && token_color != PaletteIndex::PreprocIdentifier) || flags.mBits.mDeactivated) { if (flags.mBits.mDeactivated && flags.mBits.mPreprocessor) { token_color = PaletteIndex::PreprocessorDeactivated; token_begin -= 1; @@ -2769,9 +2999,9 @@ void TextEditor::ColorizeRange() { } auto flag = line.mFlags[token_offset]; - token_length = line.mFlags.find_first_not_of(flag, token_offset + 1); - if (token_length == std::string::npos) - token_length = line.size() - token_offset; + token_length = line.mFlags.find_first_not_of(flag, token_offset + 1); + if (token_length == std::string::npos) + token_length = line.size() - token_offset; else token_length -= token_offset; @@ -2816,6 +3046,8 @@ void TextEditor::ColorizeInternal() { auto withinNotDef = false; auto currentLine = 0; auto commentLength = 0; + auto matchedBracket = false; + std::string brackets = "()[]{}<>"; std::vector ifDefs; ifDefs.push_back(true); @@ -2828,170 +3060,184 @@ void TextEditor::ColorizeInternal() { line.mFlags.resize(lineLength, 0); line.mColorized = false; } - //if (!line.mColorized) { - auto withinComment = false; - auto withinDocComment = false; - auto withinPreproc = false; - auto firstChar = true; // there is no other non-whitespace characters in the line before - auto setGlyphFlags = [&](int index) { - Line::Flags flags(0); - flags.mBits.mComment = withinComment; - flags.mBits.mBlockComment = withinBlockComment; - flags.mBits.mDocComment = withinDocComment; - flags.mBits.mGlobalDocComment = withinGlobalDocComment; - flags.mBits.mBlockDocComment = withinBlockDocComment; - flags.mBits.mDeactivated = withinNotDef; - if (mLines[currentLine].mFlags[index] != flags.mValue) { - mLines[currentLine].mColorized = false; - mLines[currentLine].mFlags[index] = flags.mValue; + auto withinComment = false; + auto withinDocComment = false; + auto withinPreproc = false; + auto firstChar = true; // there is no other non-whitespace characters in the line before + + auto setGlyphFlags = [&](int index) { + Line::Flags flags(0); + flags.mBits.mComment = withinComment; + flags.mBits.mBlockComment = withinBlockComment; + flags.mBits.mDocComment = withinDocComment; + flags.mBits.mGlobalDocComment = withinGlobalDocComment; + flags.mBits.mBlockDocComment = withinBlockDocComment; + flags.mBits.mDeactivated = withinNotDef; + flags.mBits.mMatchedBracket = matchedBracket; + if (mLines[currentLine].mFlags[index] != flags.mValue) { + mLines[currentLine].mColorized = false; + mLines[currentLine].mFlags[index] = flags.mValue; + } + }; + + auto currentIndex = 0; + if (line.empty()) + continue; + while (currentIndex < lineLength) { + + auto &g = line[currentIndex]; + auto c = g; + + matchedBracket = false; + if (MatchedBracket::mSeparators.contains(c) && mMatchedBracket.IsActive()) { + if (mMatchedBracket.mNearCursor == Coordinates(currentLine, currentIndex) || mMatchedBracket.mMatched == Coordinates(currentLine, currentIndex)) + matchedBracket = true; + } else if (MatchedBracket::mOperators.contains(c) && mMatchedBracket.IsActive()) { + if (mMatchedBracket.mNearCursor == Coordinates(currentLine, currentIndex) || mMatchedBracket.mMatched == Coordinates(currentLine, currentIndex)) { + if ((c == '<' && line.mColors[currentIndex - 1] == static_cast(PaletteIndex::UserDefinedType)) || + (c == '>' && (mMatchedBracket.mMatched.mColumn > 0 && line.mColors[mMatchedBracket.mMatched.mColumn - 1] == static_cast(PaletteIndex::UserDefinedType)) || + (mMatchedBracket.mNearCursor.mColumn > 0 && line.mColors[mMatchedBracket.mNearCursor.mColumn - 1] == static_cast(PaletteIndex::UserDefinedType)))) { + matchedBracket = true; + } } - }; + } - auto currentIndex = 0; - if (line.empty()) - continue; - while (currentIndex < lineLength) { - auto &g = line[currentIndex]; - auto c = g; + if (c != mLanguageDefinition.mPreprocChar && !isspace(c)) + firstChar = false; - if (c != mLanguageDefinition.mPreprocChar && !isspace(c)) - firstChar = false; + bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); - bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); - - if (withinString) { + if (withinString) { + setGlyphFlags(currentIndex); + if (c == '\\') { + currentIndex++; setGlyphFlags(currentIndex); - if (c == '\\') { - currentIndex++; - setGlyphFlags(currentIndex); - } else if (c == '\"') - withinString = false; - } else { - if (firstChar && c == mLanguageDefinition.mPreprocChar && !inComment && !withinComment && !withinDocComment && !withinString) { - withinPreproc = true; - std::string directive; - auto start = currentIndex + 1; - while (start < (int) line.size() && !isspace(line[start])) { - directive += line[start]; - start++; - } - - while (start < (int) line.size() && isspace(line[start])) - start++; - - if (directive == "endif" && !ifDefs.empty()) { - ifDefs.pop_back(); - withinNotDef = !ifDefs.back(); - } else { - std::string identifier; - while (start < (int) line.size() && !isspace(line[start])) { - identifier += line[start]; - start++; - } - if (directive == "define") { - if (identifier.size() > 0 && !withinNotDef && std::find(mDefines.begin(), mDefines.end(), identifier) == mDefines.end()) - mDefines.push_back(identifier); - } else if (directive == "undef") { - if (identifier.size() > 0 && !withinNotDef) - mDefines.erase(std::remove(mDefines.begin(), mDefines.end(), identifier), mDefines.end()); - } else if (directive == "ifdef") { - if (!withinNotDef) { - bool isConditionMet = std::find(mDefines.begin(), mDefines.end(), identifier) != mDefines.end(); - ifDefs.push_back(isConditionMet); - } else - ifDefs.push_back(false); - } else if (directive == "ifndef") { - if (!withinNotDef) { - bool isConditionMet = std::find(mDefines.begin(), mDefines.end(), identifier) == mDefines.end(); - ifDefs.push_back(isConditionMet); - } else - ifDefs.push_back(false); - } - } + } else if (c == '\"') + withinString = false; + } else { + if (firstChar && c == mLanguageDefinition.mPreprocChar && !inComment && !withinComment && !withinDocComment && !withinString) { + withinPreproc = true; + std::string directive; + auto start = currentIndex + 1; + while (start < (int) line.size() && !isspace(line[start])) { + directive += line[start]; + start++; } - if (c == '\"' && !withinPreproc && !inComment && !withinComment && !withinDocComment) { - withinString = true; - setGlyphFlags(currentIndex); + while (start < (int) line.size() && isspace(line[start])) + start++; + + if (directive == "endif" && !ifDefs.empty()) { + ifDefs.pop_back(); + withinNotDef = !ifDefs.back(); } else { - auto pred = [](const char &a, const char &b) { return a == b; }; + std::string identifier; + while (start < (int) line.size() && !isspace(line[start])) { + identifier += line[start]; + start++; + } + if (directive == "define") { + if (identifier.size() > 0 && !withinNotDef && std::find(mDefines.begin(), mDefines.end(), identifier) == mDefines.end()) + mDefines.push_back(identifier); + } else if (directive == "undef") { + if (identifier.size() > 0 && !withinNotDef) + mDefines.erase(std::remove(mDefines.begin(), mDefines.end(), identifier), mDefines.end()); + } else if (directive == "ifdef") { + if (!withinNotDef) { + bool isConditionMet = std::find(mDefines.begin(), mDefines.end(), identifier) != mDefines.end(); + ifDefs.push_back(isConditionMet); + } else + ifDefs.push_back(false); + } else if (directive == "ifndef") { + if (!withinNotDef) { + bool isConditionMet = std::find(mDefines.begin(), mDefines.end(), identifier) == mDefines.end(); + ifDefs.push_back(isConditionMet); + } else + ifDefs.push_back(false); + } + } + } - auto compareForth = [&](const std::string &a, const std::string &b) { - return !a.empty() && (currentIndex + a.size() <= b.size()) && equals(a.begin(), a.end(), b.begin() + currentIndex, b.begin() + (currentIndex + a.size()), pred); - }; + if (c == '\"' && !withinPreproc && !inComment && !withinComment && !withinDocComment) { + withinString = true; + setGlyphFlags(currentIndex); + } else { + auto pred = [](const char &a, const char &b) { return a == b; }; - auto compareBack = [&](const std::string &a, const std::string &b) { - return !a.empty() && currentIndex + 1 >= (int) a.size() && equals(a.begin(), a.end(), b.begin() + (currentIndex + 1 - a.size()), b.begin() + (currentIndex + 1), pred); - }; + auto compareForth = [&](const std::string &a, const std::string &b) { + return !a.empty() && (currentIndex + a.size() <= b.size()) && equals(a.begin(), a.end(), b.begin() + currentIndex, b.begin() + (currentIndex + a.size()), pred); + }; - if (!inComment && !withinComment && !withinDocComment && !withinPreproc && !withinString) { - if (compareForth(mLanguageDefinition.mDocComment, line.mChars)) { - withinDocComment = !inComment; - commentLength = 3; - } else if (compareForth(mLanguageDefinition.mSingleLineComment, line.mChars)) { - withinComment = !inComment; - commentLength = 2; - } else { - bool isGlobalDocComment = compareForth(mLanguageDefinition.mGlobalDocComment, line.mChars); - bool isBlockDocComment = compareForth(mLanguageDefinition.mBlockDocComment, line.mChars); - bool isBlockComment = compareForth(mLanguageDefinition.mCommentStart, line.mChars); - if (isGlobalDocComment || isBlockDocComment || isBlockComment) { - commentStartLine = currentLine; - commentStartIndex = currentIndex; - if (currentIndex < line.size() - 4 && isBlockComment && - line.mChars[currentIndex + 2] == '*' && - line.mChars[currentIndex + 3] == '/') { - withinBlockComment = true; - commentLength = 2; - } else if (isGlobalDocComment) { - withinGlobalDocComment = true; - commentLength = 3; - } else if (isBlockDocComment) { - withinBlockDocComment = true; - commentLength = 3; - } else { - withinBlockComment = true; - commentLength = 2; - } + auto compareBack = [&](const std::string &a, const std::string &b) { + return !a.empty() && currentIndex + 1 >= (int) a.size() && equals(a.begin(), a.end(), b.begin() + (currentIndex + 1 - a.size()), b.begin() + (currentIndex + 1), pred); + }; + + if (!inComment && !withinComment && !withinDocComment && !withinPreproc && !withinString) { + if (compareForth(mLanguageDefinition.mDocComment, line.mChars)) { + withinDocComment = !inComment; + commentLength = 3; + } else if (compareForth(mLanguageDefinition.mSingleLineComment, line.mChars)) { + withinComment = !inComment; + commentLength = 2; + } else { + bool isGlobalDocComment = compareForth(mLanguageDefinition.mGlobalDocComment, line.mChars); + bool isBlockDocComment = compareForth(mLanguageDefinition.mBlockDocComment, line.mChars); + bool isBlockComment = compareForth(mLanguageDefinition.mCommentStart, line.mChars); + if (isGlobalDocComment || isBlockDocComment || isBlockComment) { + commentStartLine = currentLine; + commentStartIndex = currentIndex; + if (currentIndex < line.size() - 4 && isBlockComment && + line.mChars[currentIndex + 2] == '*' && + line.mChars[currentIndex + 3] == '/') { + withinBlockComment = true; + commentLength = 2; + } else if (isGlobalDocComment) { + withinGlobalDocComment = true; + commentLength = 3; + } else if (isBlockDocComment) { + withinBlockDocComment = true; + commentLength = 3; + } else { + withinBlockComment = true; + commentLength = 2; } } - inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); } - setGlyphFlags(currentIndex); + inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); + } + setGlyphFlags(currentIndex); - if (compareBack(mLanguageDefinition.mCommentEnd, line.mChars) && ((commentStartLine != currentLine) || (commentStartIndex + commentLength < currentIndex))) { - withinBlockComment = false; - withinBlockDocComment = false; - withinGlobalDocComment = false; - commentStartLine = endLine; - commentStartIndex = 0; - commentLength = 0; - } + if (compareBack(mLanguageDefinition.mCommentEnd, line.mChars) && ((commentStartLine != currentLine) || (commentStartIndex + commentLength < currentIndex))) { + withinBlockComment = false; + withinBlockDocComment = false; + withinGlobalDocComment = false; + commentStartLine = endLine; + commentStartIndex = 0; + commentLength = 0; } } - if (currentIndex < line.size()) { - Line::Flags flags(0); - flags.mValue = mLines[currentLine].mFlags[currentIndex]; - flags.mBits.mPreprocessor = withinPreproc; + } + if (currentIndex < line.size()) { + Line::Flags flags(0); + flags.mValue = mLines[currentLine].mFlags[currentIndex]; + flags.mBits.mPreprocessor = withinPreproc; + mLines[currentLine].mFlags[currentIndex] = flags.mValue; + } + auto utf8CharLength = UTF8CharLength(c); + if (utf8CharLength > 1) { + Line::Flags flags(0); + flags.mValue = mLines[currentLine].mFlags[currentIndex]; + for (int j = 1; j < utf8CharLength; j++) { + currentIndex++; mLines[currentLine].mFlags[currentIndex] = flags.mValue; } - auto utf8CharLength = UTF8CharLength(c); - if (utf8CharLength > 1) { - Line::Flags flags(0); - flags.mValue = mLines[currentLine].mFlags[currentIndex]; - for (int j = 1; j < utf8CharLength; j++) { - currentIndex++; - mLines[currentLine].mFlags[currentIndex] = flags.mValue; - } - } - currentIndex++; } - withinNotDef = !ifDefs.back(); - // } - // mUpdateFlags = false; + currentIndex++; + } + withinNotDef = !ifDefs.back(); } mDefines.clear(); } diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 170eee354..15e1fdb89 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -1037,6 +1037,7 @@ "hex.builtin.view.pattern_editor.shortcut.move_home": "Move Cursor to the Start of the Line", "hex.builtin.view.pattern_editor.shortcut.move_end": "Move Cursor to the End of the Line", "hex.builtin.view.pattern_editor.shortcut.move_top": "Move Cursor to the Start of the File", + "hex.builtin.view.pattern_editor.shortcut.move_matched_bracket": "Move Cursor to the Matching Bracket", "hex.builtin.view.pattern_editor.shortcut.move_bottom": "Move Cursor to the End of the File", "hex.builtin.view.pattern_editor.shortcut.delete_word_left": "Delete One Word to the Left of the Cursor", "hex.builtin.view.pattern_editor.shortcut.delete_word_right": "Delete One Word to the Right of the Cursor", diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 03af78877..1c5fd1fb1 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -2513,6 +2513,11 @@ namespace hex::plugin::builtin { editor->MoveEnd(false); }); + ShortcutManager::addShortcut(this, CTRLCMD + SHIFT + Keys::M + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_matched_bracket", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveToMatchedBracket(false); + }); + ShortcutManager::addShortcut(this, Keys::F8 + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.add_breakpoint", [this] { const auto line = m_textEditor.get(ImHexApi::Provider::get()).GetCursorPosition().mLine + 1; const auto &runtime = ContentRegistry::PatternLanguage::getRuntime();