From 382a62343d6049585742b2cda9a1f7dfab351d52 Mon Sep 17 00:00:00 2001 From: paxcut <53811119+paxcut@users.noreply.github.com> Date: Sun, 24 Nov 2024 04:06:44 -0700 Subject: [PATCH] fix: Fixes for breakpoints (#1923) WARNING: this PR won't compile unless [PR 132 from pattern language repository](https://github.com/WerWolv/PatternLanguage/pull/132) is merged first. Some changes here are shared by at least another PR to this repository but there should not be any conflicts as the shared changes are identical. ### Problem description fix: Editing patterns with breakpoints sets behaves unexpectedly. As a simple example, set a breakpoint and insert a blank line somewhere before the breakpoint location. The breakpoint will appear to move but in reality it hasn't. To see this set another breakpoint elsewhere in the file and the old one will be displayed where it is really located at. The reason for this and many other problems with breakpoints is that currently ImHex keeps two set of breakpoints in text editor and in evaluator that are independent of each other, ie, changes to one don't affect the other. This PR aims at synchronizing the two sets through the per provider breakpoints that exist in view pattern editor. ### Implementation description It accomplishes this by making the text editor version of breakpoints the principal source of vectors and the ones in evaluator the effective version. The first allows one to modify the text around and at the breakpoint and notify others that the changes have induced changes in the breakpoint locations. The effective breakpoints allow the insertion and deletion of breakpoints. View pattern editor is where breakpoints are updated. It receives notifications from text editor about changes and then makes sure the version in evaluator is updated with those changes. View pattern editor also manages breakpoint addition and deletion so before making changes it gets a copy of the current ones from text editor, sets the ones in evaluator, uses the evaluator functions to add or delete breakpoints and finally sets the text editor version with the new version. --- .../ColorTextEditor/include/TextEditor.h | 5 ++- .../ColorTextEditor/source/TextEditor.cpp | 40 +++++++++++++------ .../content/views/view_pattern_editor.hpp | 1 + .../content/views/view_pattern_editor.cpp | 29 +++++++++----- 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h index 7bac39a95..4439730c5 100644 --- a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h +++ b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h @@ -133,7 +133,7 @@ public: using Keywords = std::unordered_set ; using ErrorMarkers = std::map>; using ErrorHoverBoxes = std::map>; - using Breakpoints = std::unordered_set; + using Breakpoints = std::unordered_set; using Palette = std::array; using Char = uint8_t ; @@ -229,6 +229,8 @@ public: bool IsReadOnly() const { return mReadOnly; } bool IsTextChanged() const { return mTextChanged; } bool IsCursorPositionChanged() const { return mCursorPositionChanged; } + bool IsBreakpointsChanged() const { return mBreakPointsChanged; } + void ClearBreakpointsChanged() { mBreakPointsChanged = false; } void SetShowCursor(bool aValue) { mShowCursor = aValue; } void SetShowLineNumbers(bool aValue) { mShowLineNumbers = aValue; } @@ -468,6 +470,7 @@ private: float mTextStart; // position (in pixels) where a code line starts relative to the left of the TextEditor. int mLeftMargin; bool mCursorPositionChanged; + bool mBreakPointsChanged; int mColorRangeMin, mColorRangeMax; SelectionMode mSelectionMode; bool mHandleKeyboardInputs; diff --git a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp index 0c0da39df..759dd73c3 100644 --- a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp +++ b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp @@ -592,12 +592,17 @@ void TextEditor::RemoveLine(int aStart, int aEnd) { mErrorMarkers = std::move(etmp); Breakpoints btmp; - for (auto i : mBreakpoints) { - if (i >= aStart && i <= aEnd) - continue; - btmp.insert(i >= aStart ? i - 1 : i); + for (auto breakpoint : mBreakpoints) { + if (breakpoint <= aStart || breakpoint >= aEnd) { + if (breakpoint >= aEnd) { + btmp.insert(breakpoint - 1); + mBreakPointsChanged = true; + } else + btmp.insert(breakpoint); + } } - mBreakpoints = std::move(btmp); + if (mBreakPointsChanged) + mBreakpoints = std::move(btmp); if (aStart == 0 && aEnd == (int32_t)mLines.size() - 1) mLines.erase(mLines.begin() + aStart, mLines.end()); else @@ -620,12 +625,15 @@ void TextEditor::RemoveLine(int aIndex) { mErrorMarkers = std::move(etmp); Breakpoints btmp; - for (auto i : mBreakpoints) { - if (i == aIndex) - continue; - btmp.insert(i >= aIndex ? i - 1 : i); + for (auto breakpoint : mBreakpoints) { + if (breakpoint > aIndex) { + btmp.insert(breakpoint - 1); + mBreakPointsChanged = true; + }else + btmp.insert(breakpoint); } - mBreakpoints = std::move(btmp); + if (mBreakPointsChanged) + mBreakpoints = std::move(btmp); mLines.erase(mLines.begin() + aIndex); assert(!mLines.empty()); @@ -642,9 +650,15 @@ TextEditor::Line &TextEditor::InsertLine(int aIndex) { mErrorMarkers = std::move(etmp); Breakpoints btmp; - for (auto i : mBreakpoints) - btmp.insert(i >= aIndex ? i + 1 : i); - mBreakpoints = std::move(btmp); + for (auto breakpoint : mBreakpoints) { + if (breakpoint >= aIndex) { + btmp.insert(breakpoint + 1); + mBreakPointsChanged = true; + } else + btmp.insert(breakpoint); + } + if (mBreakPointsChanged) + mBreakpoints = std::move(btmp); return result; } diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index 29f9497b4..11564352e 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -251,6 +251,7 @@ namespace hex::plugin::builtin { std::mutex m_logMutex; PerProvider m_cursorPosition; + PerProvider m_consoleCursorPosition; PerProvider m_selection; PerProvider m_consoleSelection; diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 006b9c62d..52cf3f97d 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -562,6 +562,16 @@ namespace hex::plugin::builtin { } } + if (m_textEditor.IsBreakpointsChanged()) { + m_breakpoints = m_textEditor.GetBreakpoints(); + m_textEditor.ClearBreakpointsChanged(); + const auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); + auto &evaluator = runtime.getInternals().evaluator; + if (evaluator) { + evaluator->setBreakpoints(m_breakpoints); + } + } + if (m_textEditor.IsTextChanged()) { m_hasUnevaluatedChanges = true; m_lastEditorChangeTime = std::chrono::steady_clock::now(); @@ -1249,23 +1259,23 @@ namespace hex::plugin::builtin { if (ImGui::BeginChild("##debugger", size, true)) { auto &evaluator = runtime.getInternals().evaluator; - const auto &breakpoints = evaluator->getBreakpoints(); + m_breakpoints = m_textEditor.GetBreakpoints(); + evaluator->setBreakpoints(m_breakpoints); const auto line = m_textEditor.GetCursorPosition().mLine + 1; - if (!breakpoints.contains(line)) { + if (!m_breakpoints->contains(line)) { if (ImGuiExt::IconButton(ICON_VS_DEBUG_BREAKPOINT, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) { evaluator->addBreakpoint(line); - m_textEditor.SetBreakpoints(breakpoints); } ImGuiExt::InfoTooltip("hex.builtin.view.pattern_editor.debugger.add_tooltip"_lang); } else { if (ImGuiExt::IconButton(ICON_VS_DEBUG_BREAKPOINT_UNVERIFIED, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) { evaluator->removeBreakpoint(line); - m_textEditor.SetBreakpoints(breakpoints); } ImGuiExt::InfoTooltip("hex.builtin.view.pattern_editor.debugger.remove_tooltip"_lang); } - + m_breakpoints = evaluator->getBreakpoints(); + m_textEditor.SetBreakpoints(m_breakpoints); ImGui::SameLine(); if (*m_breakpointHit) { @@ -2397,15 +2407,16 @@ namespace hex::plugin::builtin { const auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); auto &evaluator = runtime.getInternals().evaluator; - auto &breakpoints = evaluator->getBreakpoints(); + m_breakpoints = m_textEditor.GetBreakpoints(); + evaluator->setBreakpoints(m_breakpoints); - if (breakpoints.contains(line)) { + if (m_breakpoints->contains(line)) { evaluator->removeBreakpoint(line); } else { evaluator->addBreakpoint(line); } - - m_textEditor.SetBreakpoints(breakpoints); + m_breakpoints = evaluator->getBreakpoints(); + m_textEditor.SetBreakpoints(m_breakpoints); }); /* Trigger evaluation */