From 57c2d8412205703b859a6a06e51985ced640c16c Mon Sep 17 00:00:00 2001 From: paxcut <53811119+paxcut@users.noreply.github.com> Date: Sat, 17 May 2025 11:26:54 -0700 Subject: [PATCH] improv: made the text editors to be per provider. (#2255) The recent update that made importing patterns undoable had the side effect of undoing tab changes as well. When working on a fix for that it became clear that the undo/redo stacks were being shared by all providers so that you could undo changes done in a separate file. The problem was fixed by making the text editors (and the console editors as well) to be per provider which gives better control on things like breakpoints and selections and a more robust focusing system that preserves the cursor positions and removes the need to click on a pattern to start editing. There are a lot of changes, but they are mostly all the uses of text editors being now coded differently to manage the providers. File imports are still undoable, but switching providers is not. Further changes suggested by reviewer were implemented namely a mull provider was being used and there was a get function missing which prevented the use of the preferred syntax. --- .../ColorTextEditor/include/TextEditor.h | 4 +- .../ColorTextEditor/source/TextEditor.cpp | 40 +-- .../content/views/view_pattern_editor.hpp | 21 +- .../content/views/view_pattern_editor.cpp | 283 ++++++++++-------- 4 files changed, 188 insertions(+), 160 deletions(-) diff --git a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h index 5b0ddbd86..faf810498 100644 --- a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h +++ b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h @@ -292,7 +292,7 @@ public: ImVec2 Underwaves( ImVec2 pos, uint32_t nChars, ImColor color= ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg= ImVec2(0, 0)); void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); - void SetText(const std::string& aText); + void SetText(const std::string& aText, bool aUndo = false); void JumpToLine(int line=-1); void JumpToCoords(const Coordinates &coords); void SetLongestLineLength(size_t line) { @@ -338,7 +338,7 @@ public: mNewTopMargin = newMargin; mTopMarginChanged = true; } - void setFocusAtCoords(const Coordinates &coords) { + void SetFocusAtCoords(const Coordinates &coords) { mFocusAtCoords = coords; mUpdateFocus = true; } diff --git a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp index a53e00a52..3f41351c5 100644 --- a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp +++ b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp @@ -874,10 +874,9 @@ void TextEditor::SetFocus() { mState.mCursorPosition = mFocusAtCoords; ResetCursorBlinkTime(); EnsureCursorVisible(); - if (!this->mReadOnly) { + if (!this->mReadOnly) ImGui::SetKeyboardFocusHere(0); - mUpdateFocus = false; - } + mUpdateFocus = false; } void TextEditor::RenderText(const char *aTitle, const ImVec2 &lineNumbersStartPos, const ImVec2 &textEditorSize) { @@ -1091,7 +1090,7 @@ void TextEditor::RenderText(const char *aTitle, const ImVec2 &lineNumbersStartPo auto prevColor = line.empty() ? mPalette[(int)PaletteIndex::Default] : GetGlyphColor(line[0]); ImVec2 bufferOffset; - if (mUpdateFocus && mFocusAtCoords == Coordinates(lineNo, 0)) { + if (mUpdateFocus) { SetFocus(); } @@ -1132,10 +1131,6 @@ void TextEditor::RenderText(const char *aTitle, const ImVec2 &lineNumbersStartPo prevColor = color; - if (mUpdateFocus && mFocusAtCoords == Coordinates(lineNo, i)) { - SetFocus(); - } - if (glyph.mChar == '\t') { auto oldX = bufferOffset.x; bufferOffset.x = (1.0f + std::floor((1.0f + bufferOffset.x) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); @@ -1297,12 +1292,14 @@ void TextEditor::Render(const char *aTitle, const ImVec2 &aSize, bool aBorder) { ImGui::Dummy({}); } -void TextEditor::SetText(const std::string &aText) { +void TextEditor::SetText(const std::string &aText, bool aUndo) { UndoRecord u; - u.mBefore = mState; - u.mRemoved = GetText(); - u.mRemovedStart = Coordinates(0, 0); - u.mRemovedEnd = Coordinates((int)mLines.size()-1, GetLineMaxColumn((int)mLines.size()-1)); + if (!mReadOnly && aUndo) { + u.mBefore = mState; + u.mRemoved = GetText(); + u.mRemovedStart = Coordinates(0, 0); + u.mRemovedEnd = Coordinates((int) mLines.size() - 1, GetLineMaxColumn((int) mLines.size() - 1)); + } mLines.resize(1); mLines[0].clear(); std::string text = PreprocessText(aText); @@ -1312,14 +1309,18 @@ void TextEditor::SetText(const std::string &aText) { else mLines.back().push_back(Glyph(chr, PaletteIndex::Default)); } - u.mAdded = text; - u.mAddedStart = Coordinates(0, 0); - u.mAddedEnd = Coordinates((int)mLines.size()-1, GetLineMaxColumn((int)mLines.size()-1)); + if (!mReadOnly && aUndo) { + u.mAdded = text; + u.mAddedStart = Coordinates(0, 0); + u.mAddedEnd = Coordinates((int) mLines.size() - 1, GetLineMaxColumn((int) mLines.size() - 1)); + } mTextChanged = true; mScrollToTop = true; - u.mAfter = mState; - if (!mReadOnly) + if (!mReadOnly && aUndo) { + u.mAfter = mState; + AddUndo(u); + } Colorize(); } @@ -1647,7 +1648,7 @@ void TextEditor::JumpToCoords(const Coordinates &aNewPos) { SetCursorPosition(aNewPos); EnsureCursorVisible(); - setFocusAtCoords(aNewPos); + SetFocusAtCoords(aNewPos); } void TextEditor::MoveUp(int aAmount, bool aSelect) { @@ -3085,7 +3086,6 @@ void TextEditor::EnsureCursorVisible() { mScrollToCursorX = false; if (!mScrollToCursorX && !mScrollToCursorY && mOldTopMargin == mTopMargin) { mScrollToCursor = false; - mOldTopMargin = mTopMargin; return; } diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index f52a97422..e9ee87b5f 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -34,18 +34,18 @@ namespace hex::plugin::builtin { class PatternSourceCode { public: - const std::string& get(prv::Provider *provider) { + const std::string& get(prv::Provider *provider) const { if (m_synced) return m_sharedSource; return m_perProviderSource.get(provider); } - void set(prv::Provider *provider, std::string source) { - source = wolv::util::trim(source); + std::string& get(prv::Provider *provider) { + if (m_synced) + return m_sharedSource; - m_perProviderSource.set(source, provider); - m_sharedSource = std::move(source); + return m_perProviderSource.get(provider); } bool isSynced() const { @@ -233,10 +233,10 @@ namespace hex::plugin::builtin { std::atomic m_runningEvaluators = 0; std::atomic m_runningParsers = 0; - bool m_hasUnevaluatedChanges = false; + PerProvider m_hasUnevaluatedChanges; std::chrono::time_point m_lastEditorChangeTime; - TextEditor m_textEditor, m_consoleEditor; + PerProvider m_textEditor, m_consoleEditor; std::atomic m_consoleNeedsUpdate = false; std::atomic m_dangerousFunctionCalled = false; @@ -259,6 +259,8 @@ namespace hex::plugin::builtin { PerProvider m_cursorPosition; PerProvider m_consoleCursorPosition; + PerProvider m_cursorNeedsUpdate; + PerProvider m_consoleCursorNeedsUpdate; PerProvider m_selection; PerProvider m_consoleSelection; PerProvider m_consoleLongestLineLength; @@ -379,11 +381,12 @@ namespace hex::plugin::builtin { }; std::function m_exportPatternFile = [this] { + auto provider = ImHexApi::Provider::get(); fs::openFileBrowser( fs::DialogMode::Save, { {"Pattern", "hexpat"} }, - [this](const auto &path) { + [this, provider](const auto &path) { wolv::io::File file(path, wolv::io::File::Mode::Create); - file.writeString(wolv::util::trim(m_textEditor.GetText())); + file.writeString(wolv::util::trim(m_textEditor.get(provider).GetText())); } ); }; diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 71810f55e..a7e6f9c37 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -211,21 +211,6 @@ namespace hex::plugin::builtin { m_editorRuntime = std::make_unique(); ContentRegistry::PatternLanguage::configureRuntime(*m_editorRuntime, nullptr); - m_textEditor.SetLanguageDefinition(PatternLanguage()); - m_textEditor.SetShowWhitespaces(false); - - m_consoleEditor.SetLanguageDefinition(ConsoleLog()); - m_consoleEditor.SetShowWhitespaces(false); - m_consoleEditor.SetReadOnly(true); - m_consoleEditor.SetShowCursor(false); - m_consoleEditor.SetShowLineNumbers(false); - m_consoleEditor.SetSourceCodeEditor(&m_textEditor); - std::string sourcecode = pl::api::Source::DefaultSource; - std::string error = "E: "; - std::string end = ":"; - std::string arrow = " --> in "; - m_consoleEditor.AddClickableText(error + sourcecode + end); - m_consoleEditor.AddClickableText(error + arrow + sourcecode + end); this->registerEvents(); this->registerMenuItems(); @@ -357,15 +342,20 @@ namespace hex::plugin::builtin { } ImGui::PushFont(fonts::CodeEditor()); - m_textEditor.Render("##pattern_editor", textEditorSize, false); + m_textEditor.get(provider).Render("##pattern_editor", textEditorSize, false); ImGui::PopFont(); m_textEditorHoverBox = ImRect(windowPosition,windowPosition+textEditorSize); m_consoleHoverBox = ImRect(ImVec2(windowPosition.x,windowPosition.y+textEditorSize.y),windowPosition+availableSize); - TextEditor::FindReplaceHandler *findReplaceHandler = m_textEditor.GetFindReplaceHandler(); - if (m_textEditor.RaiseContextMenu()) { + TextEditor::FindReplaceHandler *findReplaceHandler = m_textEditor.get(provider).GetFindReplaceHandler(); + if (m_textEditor.get(provider).RaiseContextMenu()) { ImGui::OpenPopup("##text_editor_context_menu"); - m_textEditor.ClearRaiseContextMenu(); + m_textEditor.get(provider).ClearRaiseContextMenu(); + } + + if (m_cursorNeedsUpdate.get(provider)) { + m_textEditor.get(provider).SetFocusAtCoords(m_cursorPosition.get(provider)); + m_cursorNeedsUpdate.get(provider) = false; } if (ImGui::BeginPopup("##text_editor_context_menu")) { @@ -377,26 +367,26 @@ namespace hex::plugin::builtin { ImGui::Separator(); - if (!m_textEditor.HasSelection()) - m_textEditor.SelectWordUnderCursor(); - const bool hasSelection = m_textEditor.HasSelection(); + if (!m_textEditor.get(provider).HasSelection()) + m_textEditor.get(provider).SelectWordUnderCursor(); + const bool hasSelection = m_textEditor.get(provider).HasSelection(); if (ImGui::MenuItemEx("hex.builtin.view.hex_editor.menu.edit.cut"_lang, ICON_VS_COMBINE, Shortcut(CTRLCMD + Keys::X).toString().c_str(), false, hasSelection)) { - m_textEditor.Cut(); + m_textEditor.get(provider).Cut(); } if (ImGui::MenuItemEx("hex.builtin.view.hex_editor.menu.edit.copy"_lang, ICON_VS_COPY, Shortcut(CTRLCMD + Keys::C).toString().c_str(), false, hasSelection)) { - m_textEditor.Copy(); + m_textEditor.get(provider).Copy(); } if (ImGui::MenuItemEx("hex.builtin.view.hex_editor.menu.edit.paste"_lang, ICON_VS_OUTPUT, Shortcut(CTRLCMD + Keys::V).toString().c_str())) { - m_textEditor.Paste(); + m_textEditor.get(provider).Paste(); } ImGui::Separator(); - if (ImGui::MenuItemEx("hex.builtin.menu.edit.undo"_lang, ICON_VS_DISCARD, Shortcut(CTRLCMD + Keys::Z).toString().c_str(), false, m_textEditor.CanUndo())) { - m_textEditor.Undo(); + if (ImGui::MenuItemEx("hex.builtin.menu.edit.undo"_lang, ICON_VS_DISCARD, Shortcut(CTRLCMD + Keys::Z).toString().c_str(), false, m_textEditor.get(provider).CanUndo())) { + m_textEditor.get(provider).Undo(); } - if (ImGui::MenuItemEx("hex.builtin.menu.edit.redo"_lang, ICON_VS_REDO, Shortcut(CTRLCMD + Keys::Y).toString().c_str(), false, m_textEditor.CanRedo())) { - m_textEditor.Redo(); + if (ImGui::MenuItemEx("hex.builtin.menu.edit.redo"_lang, ICON_VS_REDO, Shortcut(CTRLCMD + Keys::Y).toString().c_str(), false, m_textEditor.get(provider).CanRedo())) { + m_textEditor.get(provider).Redo(); } ImGui::Separator(); @@ -408,10 +398,10 @@ namespace hex::plugin::builtin { if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.find_next"_lang, Shortcut(Keys::F3).toString().c_str(),false,!findReplaceHandler->GetFindWord().empty())) - findReplaceHandler->FindMatch(&m_textEditor,true); + findReplaceHandler->FindMatch(&m_textEditor.get(provider),true); if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.find_previous"_lang, Shortcut(SHIFT + Keys::F3).toString().c_str(),false,!findReplaceHandler->GetFindWord().empty())) - findReplaceHandler->FindMatch(&m_textEditor,false); + findReplaceHandler->FindMatch(&m_textEditor.get(provider),false); if (ImGui::MenuItemEx("hex.builtin.view.pattern_editor.menu.replace"_lang, ICON_VS_REPLACE, Shortcut(CTRLCMD + Keys::H).toString().c_str())) { m_replaceMode = true; @@ -419,13 +409,13 @@ namespace hex::plugin::builtin { } if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.replace_next"_lang,"",false,!findReplaceHandler->GetReplaceWord().empty())) - findReplaceHandler->Replace(&m_textEditor,true); + findReplaceHandler->Replace(&m_textEditor.get(provider),true); if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.replace_previous"_lang, "",false,!findReplaceHandler->GetReplaceWord().empty())) - findReplaceHandler->Replace(&m_textEditor,false); + findReplaceHandler->Replace(&m_textEditor.get(provider),false); if (ImGui::MenuItemEx("hex.builtin.view.pattern_editor.menu.replace_all"_lang, ICON_VS_REPLACE_ALL, "",false,!findReplaceHandler->GetReplaceWord().empty())) - findReplaceHandler->ReplaceAll(&m_textEditor); + findReplaceHandler->ReplaceAll(&m_textEditor.get(provider)); if (ImGui::MenuItemEx("hex.builtin.view.pattern_editor.menu.goto_line"_lang, ICON_VS_DEBUG_STEP_INTO, Shortcut(ALT + Keys::G).toString().c_str())) m_openGotoLinePopUp = true; @@ -510,7 +500,7 @@ namespace hex::plugin::builtin { } else { if (ImGuiExt::IconButton(ICON_VS_DEBUG_START, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || m_triggerEvaluation) { m_triggerEvaluation = false; - this->evaluatePattern(m_textEditor.GetText(), provider); + this->evaluatePattern(m_textEditor.get(provider).GetText(), provider); } } @@ -566,7 +556,7 @@ namespace hex::plugin::builtin { } else { if (ImGui::Checkbox("hex.builtin.view.pattern_editor.auto"_lang, &m_runAutomatically)) { if (m_runAutomatically) - m_hasUnevaluatedChanges = true; + m_hasUnevaluatedChanges.get(provider) = true; } ImGui::SameLine(); @@ -583,9 +573,9 @@ namespace hex::plugin::builtin { } } - if (m_textEditor.IsBreakpointsChanged()) { - m_breakpoints = m_textEditor.GetBreakpoints(); - m_textEditor.ClearBreakpointsChanged(); + if (m_textEditor.get(provider).IsBreakpointsChanged()) { + m_breakpoints = m_textEditor.get(provider).GetBreakpoints(); + m_textEditor.get(provider).ClearBreakpointsChanged(); const auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); auto &evaluator = runtime.getInternals().evaluator; if (evaluator) { @@ -593,16 +583,16 @@ namespace hex::plugin::builtin { } } - if (m_textEditor.IsTextChanged() && !m_hasUnevaluatedChanges) { - m_hasUnevaluatedChanges = true; + if (m_textEditor.get(provider).IsTextChanged() && !m_hasUnevaluatedChanges.get(provider)) { + m_hasUnevaluatedChanges.get(provider) = true; m_lastEditorChangeTime = std::chrono::steady_clock::now(); ImHexApi::Provider::markDirty(); } - if (m_hasUnevaluatedChanges && m_runningEvaluators == 0 && m_runningParsers == 0) { + if (m_hasUnevaluatedChanges.get(provider) && m_runningEvaluators == 0 && m_runningParsers == 0) { if ((std::chrono::steady_clock::now() - m_lastEditorChangeTime) > std::chrono::seconds(1LL)) { - auto code = m_textEditor.GetText(); + auto code = m_textEditor.get(provider).GetText(); EventPatternEditorChanged::post(code); TaskManager::createBackgroundTask("hex.builtin.task.parsing_pattern", [this, code = std::move(code), provider](auto &){ @@ -611,13 +601,13 @@ namespace hex::plugin::builtin { if (m_runAutomatically) m_triggerAutoEvaluate = true; }); - m_hasUnevaluatedChanges = false; - m_textEditor.SetTextChanged(); + m_hasUnevaluatedChanges.get(provider) = false; + m_textEditor.get(provider).SetTextChanged(); } } if (m_triggerAutoEvaluate.exchange(false)) { - this->evaluatePattern(m_textEditor.GetText(), provider); + this->evaluatePattern(m_textEditor.get(provider).GetText(), provider); } } @@ -653,6 +643,7 @@ namespace hex::plugin::builtin { } void ViewPatternEditor::drawTextEditorFindReplacePopup(TextEditor *textEditor) { + auto provider = ImHexApi::Provider::get(); ImGuiWindowFlags popupFlags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar; if (ImGui::BeginPopup("##text_editor_view_find_replace_popup", popupFlags)) { static std::string findWord; @@ -951,7 +942,7 @@ namespace hex::plugin::builtin { // Escape key to close the popup if (ImGui::IsKeyPressed(ImGuiKey_Escape, false)) { m_popupWindowHeight = 0; - m_textEditor.SetTopMarginChanged(0); + m_textEditor.get(provider).SetTopMarginChanged(0); ImGui::CloseCurrentPopup(); } @@ -962,7 +953,7 @@ namespace hex::plugin::builtin { auto heightChange = height - m_popupWindowHeight; auto heightChangeChange = heightChange - m_popupWindowHeightChange; if (std::fabs(heightChange) < 0.5 && std::fabs(heightChangeChange) > 1.0) { - m_textEditor.SetTopMarginChanged(height); + m_textEditor.get(provider).SetTopMarginChanged(height); } m_popupWindowHeightChange = heightChange; m_popupWindowHeight = height; @@ -973,11 +964,12 @@ namespace hex::plugin::builtin { } else if (!m_frPopupIsClosed) { m_frPopupIsClosed = true; m_popupWindowHeight = 0; - m_textEditor.SetTopMarginChanged(0); + m_textEditor.get(provider).SetTopMarginChanged(0); } } void ViewPatternEditor::drawTextEditorGotoLinePopup(TextEditor *textEditor) { + auto provider = ImHexApi::Provider::get(); ImGuiWindowFlags popupFlags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar; if (ImGui::BeginPopup("##text_editor_view_goto_line_popup", popupFlags)) { std::string childName; @@ -1010,7 +1002,7 @@ namespace hex::plugin::builtin { } if (ImGui::IsKeyPressed(ImGuiKey_Escape, false)) { m_popupWindowHeight = 0; - m_textEditor.SetTopMarginChanged(0); + m_textEditor.get(provider).SetTopMarginChanged(0); ImGui::CloseCurrentPopup(); } @@ -1021,7 +1013,7 @@ namespace hex::plugin::builtin { auto heightChange = height - m_popupWindowHeight; auto heightChangeChange = heightChange - m_popupWindowHeightChange; if (std::fabs(heightChange) < 0.5 && std::fabs(heightChangeChange) > 1.0) { - m_textEditor.SetTopMarginChanged(height); + m_textEditor.get(provider).SetTopMarginChanged(height); } m_popupWindowHeightChange = heightChange; m_popupWindowHeight = height; @@ -1032,24 +1024,25 @@ namespace hex::plugin::builtin { } else if (!m_gotoPopupIsClosed) { m_gotoPopupIsClosed = true; m_popupWindowHeight = 0; - m_textEditor.SetTopMarginChanged(0); + m_textEditor.get(provider).SetTopMarginChanged(0); } } void ViewPatternEditor::drawConsole(ImVec2 size) { - auto findReplaceHandler = m_consoleEditor.GetFindReplaceHandler(); - if (m_consoleEditor.RaiseContextMenu()) { + auto provider = ImHexApi::Provider::get(); + auto findReplaceHandler = m_consoleEditor.get(provider).GetFindReplaceHandler(); + if (m_consoleEditor.get(provider).RaiseContextMenu()) { ImGui::OpenPopup("##console_context_menu"); - m_consoleEditor.ClearRaiseContextMenu(); + m_consoleEditor.get(provider).ClearRaiseContextMenu(); } - const bool hasSelection = m_consoleEditor.HasSelection(); + const bool hasSelection = m_consoleEditor.get(provider).HasSelection(); if (ImGui::BeginPopup("##console_context_menu")) { if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.copy"_lang, Shortcut(CTRLCMD + Keys::C).toString().c_str(), false, hasSelection)) { - m_consoleEditor.Copy(); + m_consoleEditor.get(provider).Copy(); } if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.select_all"_lang, Shortcut(CTRLCMD + Keys::A).toString().c_str())) { - m_consoleEditor.SelectAll(); + m_consoleEditor.get(provider).SelectAll(); } ImGui::Separator(); // Search and replace entries @@ -1059,10 +1052,10 @@ namespace hex::plugin::builtin { } if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.find_next"_lang, Shortcut(Keys::F3).toString().c_str(),false,!findReplaceHandler->GetFindWord().empty())) - findReplaceHandler->FindMatch(&m_consoleEditor,true); + findReplaceHandler->FindMatch(&m_consoleEditor.get(provider),true); if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.find_previous"_lang, Shortcut(SHIFT + Keys::F3).toString().c_str(),false,!findReplaceHandler->GetFindWord().empty())) - findReplaceHandler->FindMatch(&m_consoleEditor,false); + findReplaceHandler->FindMatch(&m_consoleEditor.get(provider),false); ImGui::Separator(); @@ -1072,24 +1065,30 @@ namespace hex::plugin::builtin { ImGui::EndPopup(); } + + if (m_consoleCursorNeedsUpdate.get(provider)) { + m_consoleEditor.get(provider).SetFocusAtCoords(m_consoleCursorPosition.get(provider)); + m_consoleCursorNeedsUpdate.get(provider) = false; + } + if (m_consoleNeedsUpdate) { std::scoped_lock lock(m_logMutex); - auto lineCount = m_consoleEditor.GetTextLines().size(); - if (m_console->size() < lineCount || (lineCount == 1 && m_consoleEditor.GetLineText(0).empty())) { - m_consoleEditor.SetText(""); + auto lineCount = m_consoleEditor.get(provider).GetTextLines().size(); + if (m_console->size() < lineCount || (lineCount == 1 && m_consoleEditor.get(provider).GetLineText(0).empty())) { + m_consoleEditor.get(provider).SetText(""); lineCount = 0; } const auto linesToAdd = m_console->size() - lineCount; for (size_t i = 0; i < linesToAdd; i += 1) { - m_consoleEditor.AppendLine(m_console->at(lineCount + i)); + m_consoleEditor.get(provider).AppendLine(m_console->at(lineCount + i)); } m_consoleNeedsUpdate = false; } ImGui::PushFont(fonts::CodeEditor()); - m_consoleEditor.Render("##console", size, true); + m_consoleEditor.get(provider).Render("##console", size, true); ImGui::PopFont(); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y + 1_scaled); @@ -1204,6 +1203,7 @@ namespace hex::plugin::builtin { } void ViewPatternEditor::drawVariableSettings(ImVec2 size, std::map &patternVariables) { + auto provider = ImHexApi::Provider::get(); if (ImGui::BeginChild("##settings", size, true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { if (patternVariables.empty()) { ImGuiExt::TextOverlay("hex.builtin.view.pattern_editor.no_in_out_vars"_lang, ImGui::GetWindowPos() + ImGui::GetWindowSize() / 2, ImGui::GetWindowWidth() * 0.7); @@ -1230,32 +1230,32 @@ namespace hex::plugin::builtin { if (pl::core::Token::isSigned(variable.type)) { i64 value = i64(hex::get_or(variable.value, 0)); if (ImGui::InputScalar(label.c_str(), ImGuiDataType_S64, &value)) - m_hasUnevaluatedChanges = true; + m_hasUnevaluatedChanges.get(provider) = true; variable.value = i128(value); } else if (pl::core::Token::isUnsigned(variable.type)) { u64 value = u64(hex::get_or(variable.value, 0)); if (ImGui::InputScalar(label.c_str(), ImGuiDataType_U64, &value)) - m_hasUnevaluatedChanges = true; + m_hasUnevaluatedChanges.get(provider) = true; variable.value = u128(value); } else if (pl::core::Token::isFloatingPoint(variable.type)) { auto value = hex::get_or(variable.value, 0.0); if (ImGui::InputScalar(label.c_str(), ImGuiDataType_Double, &value)) - m_hasUnevaluatedChanges = true; + m_hasUnevaluatedChanges.get(provider) = true; variable.value = value; } else if (variable.type == pl::core::Token::ValueType::Boolean) { auto value = hex::get_or(variable.value, false); if (ImGui::Checkbox(label.c_str(), &value)) - m_hasUnevaluatedChanges = true; + m_hasUnevaluatedChanges.get(provider) = true; variable.value = value; } else if (variable.type == pl::core::Token::ValueType::Character) { std::array buffer = { hex::get_or(variable.value, '\x00') }; if (ImGui::InputText(label.c_str(), buffer.data(), buffer.size())) - m_hasUnevaluatedChanges = true; + m_hasUnevaluatedChanges.get(provider) = true; variable.value = buffer[0]; } else if (variable.type == pl::core::Token::ValueType::String) { std::string buffer = hex::get_or(variable.value, ""); if (ImGui::InputText(label.c_str(), buffer)) - m_hasUnevaluatedChanges = true; + m_hasUnevaluatedChanges.get(provider) = true; variable.value = buffer; } } @@ -1391,13 +1391,14 @@ namespace hex::plugin::builtin { void ViewPatternEditor::drawDebugger(ImVec2 size) { + auto provider = ImHexApi::Provider::get(); const auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); if (ImGui::BeginChild("##debugger", size, true)) { auto &evaluator = runtime.getInternals().evaluator; - m_breakpoints = m_textEditor.GetBreakpoints(); + m_breakpoints = m_textEditor.get(provider).GetBreakpoints(); evaluator->setBreakpoints(m_breakpoints); - const auto line = m_textEditor.GetCursorPosition().mLine + 1; + const auto line = m_textEditor.get(provider).GetCursorPosition().mLine + 1; if (!m_breakpoints->contains(line)) { if (ImGuiExt::IconButton(ICON_VS_DEBUG_BREAKPOINT, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) { @@ -1411,7 +1412,7 @@ namespace hex::plugin::builtin { ImGuiExt::InfoTooltip("hex.builtin.view.pattern_editor.debugger.remove_tooltip"_lang); } m_breakpoints = evaluator->getBreakpoints(); - m_textEditor.SetBreakpoints(m_breakpoints); + m_textEditor.get(provider).SetBreakpoints(m_breakpoints); ImGui::SameLine(); if (*m_breakpointHit) { @@ -1449,7 +1450,7 @@ namespace hex::plugin::builtin { m_resetDebuggerVariables = false; if (pauseLine.has_value()) - m_textEditor.JumpToLine(pauseLine.value() - 1); + m_textEditor.get(provider).JumpToLine(pauseLine.value() - 1); } const auto &currScope = evaluator->getScope(-m_debuggerScopeIndex); @@ -1521,7 +1522,7 @@ namespace hex::plugin::builtin { } } - m_textEditor.SetErrorMarkers(errorMarkers); + m_textEditor.get(provider).SetErrorMarkers(errorMarkers); } else { for (auto &[name, variable] : *m_patternVariables) { if (variable.outVariable && m_lastEvaluationOutVars->contains(name)) @@ -1838,8 +1839,8 @@ namespace hex::plugin::builtin { auto code = file.readString(); this->evaluatePattern(code, provider); - m_textEditor.SetText(code); - m_sourceCode.set(provider, code); + m_textEditor.get(provider).SetText(code, true); + m_sourceCode.get(provider) = code; TaskManager::createBackgroundTask("hex.builtin.task.parsing_pattern", [this, code, provider](auto&) { this->parsePattern(code, provider); }); } @@ -1850,7 +1851,7 @@ namespace hex::plugin::builtin { ContentRegistry::PatternLanguage::configureRuntime(*m_editorRuntime, nullptr); const auto &ast = m_editorRuntime->parseString(code, pl::api::Source::DefaultSource); - m_textEditor.SetLongestLineLength(m_editorRuntime->getInternals().preprocessor.get()->getLongestLineLength()); + m_textEditor.get(provider).SetLongestLineLength(m_editorRuntime->getInternals().preprocessor.get()->getLongestLineLength()); auto &patternVariables = m_patternVariables.get(provider); auto oldPatternVariables = std::move(patternVariables); @@ -1893,15 +1894,15 @@ namespace hex::plugin::builtin { m_runningEvaluators += 1; m_executionDone.get(provider) = false; - m_textEditor.ClearActionables(); + m_textEditor.get(provider).ClearActionables(); - m_consoleEditor.ClearActionables(); + m_consoleEditor.get(provider).ClearActionables(); m_console.get(provider).clear(); m_consoleLongestLineLength.get(provider) = 0; m_consoleNeedsUpdate = true; m_sectionWindowDrawer.clear(); - m_consoleEditor.SetText(""); + m_consoleEditor.get(provider).SetText(""); m_virtualFiles->clear(); m_accessHistory = {}; @@ -1915,13 +1916,13 @@ namespace hex::plugin::builtin { auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); ContentRegistry::PatternLanguage::configureRuntime(runtime, provider); - runtime.getInternals().evaluator->setBreakpointHitCallback([this, &runtime] { + runtime.getInternals().evaluator->setBreakpointHitCallback([this, &runtime, provider] { m_debuggerScopeIndex = 0; *m_breakpointHit = true; m_resetDebuggerVariables = true; auto optPauseLine = runtime.getInternals().evaluator->getPauseLine(); if (optPauseLine.has_value()) - m_textEditor.JumpToLine(optPauseLine.value() - 1); + m_textEditor.get(provider).JumpToLine(optPauseLine.value() - 1); while (*m_breakpointHit) { std::this_thread::sleep_for(std::chrono::milliseconds(100LL)); } @@ -1970,7 +1971,7 @@ namespace hex::plugin::builtin { } if (m_consoleLongestLineLength.get(provider) < line.size()) { m_consoleLongestLineLength.get(provider) = line.size(); - m_consoleEditor.SetLongestLineLength(line.size()); + m_consoleEditor.get(provider).SetLongestLineLength(line.size()); } m_console.get(provider).emplace_back(line); m_consoleNeedsUpdate = true; @@ -2009,11 +2010,12 @@ namespace hex::plugin::builtin { void ViewPatternEditor::registerEvents() { RequestPatternEditorSelectionChange::subscribe(this, [this](u32 line, u32 column) { + auto provider = ImHexApi::Provider::get(); if (line == 0) return; const TextEditor::Coordinates coords = { int(line) - 1, int(column) }; - m_textEditor.SetCursorPosition(coords); + m_textEditor.get(provider).SetCursorPosition(coords); }); RequestLoadPatternLanguageFile::subscribe(this, [this](const std::fs::path &path) { @@ -2025,14 +2027,16 @@ namespace hex::plugin::builtin { }); RequestSavePatternLanguageFile::subscribe(this, [this](const std::fs::path &path) { + auto provider = ImHexApi::Provider::get(); wolv::io::File file(path, wolv::io::File::Mode::Create); - file.writeString(wolv::util::trim(m_textEditor.GetText())); + file.writeString(wolv::util::trim(m_textEditor.get(provider).GetText())); }); RequestSetPatternLanguageCode::subscribe(this, [this](const std::string &code) { - m_textEditor.SetText(code); - m_sourceCode.set(ImHexApi::Provider::get(), code); - m_hasUnevaluatedChanges = true; + auto provider = ImHexApi::Provider::get(); + m_textEditor.get(provider).SetText(code); + m_sourceCode.get(provider) = code; + m_hasUnevaluatedChanges.get(provider) = true; }); ContentRegistry::Settings::onChange("hex.builtin.setting.general", "hex.builtin.setting.general.sync_pattern_source", [this](const ContentRegistry::Settings::SettingsValue &value) { @@ -2043,6 +2047,21 @@ namespace hex::plugin::builtin { }); EventProviderOpened::subscribe(this, [this](prv::Provider *provider) { + m_textEditor.get(provider).SetLanguageDefinition(PatternLanguage()); + m_textEditor.get(provider).SetShowWhitespaces(false); + + m_consoleEditor.get(provider).SetLanguageDefinition(ConsoleLog()); + m_consoleEditor.get(provider).SetShowWhitespaces(false); + m_consoleEditor.get(provider).SetReadOnly(true); + m_consoleEditor.get(provider).SetShowCursor(false); + m_consoleEditor.get(provider).SetShowLineNumbers(false); + m_consoleEditor.get(provider).SetSourceCodeEditor(&m_textEditor.get(provider)); + std::string sourcecode = pl::api::Source::DefaultSource; + std::string error = "E: "; + std::string end = ":"; + std::string arrow = " --> in "; + m_consoleEditor.get(provider).AddClickableText(error + sourcecode + end); + m_consoleEditor.get(provider).AddClickableText(error + arrow + sourcecode + end); m_shouldAnalyze.get(provider) = true; m_envVarEntries.get(provider).emplace_back(0, "", i128(0), EnvVarType::Integer); @@ -2052,33 +2071,37 @@ namespace hex::plugin::builtin { EventProviderChanged::subscribe(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) { if (oldProvider != nullptr) { - m_sourceCode.set(oldProvider, m_textEditor.GetText()); - m_cursorPosition.set(m_textEditor.GetCursorPosition(),oldProvider); - m_selection.set(m_textEditor.GetSelection(),oldProvider); - m_consoleCursorPosition.set(m_consoleEditor.GetCursorPosition(),oldProvider); - m_consoleSelection.set(m_consoleEditor.GetSelection(),oldProvider); - m_consoleLongestLineLength.set(m_consoleEditor.GetLongestLineLength(),oldProvider); - m_breakpoints.set(m_textEditor.GetBreakpoints(),oldProvider); + m_sourceCode.get(oldProvider) = m_textEditor.get(oldProvider).GetText(); + m_cursorPosition.get(oldProvider) = m_textEditor.get(oldProvider).GetCursorPosition(); + m_selection.get(oldProvider) =m_textEditor.get(oldProvider).GetSelection(); + m_consoleCursorPosition.get(oldProvider) = m_consoleEditor.get(oldProvider).GetCursorPosition(); + m_consoleSelection.get(oldProvider) = m_consoleEditor.get(oldProvider).GetSelection(); + m_consoleLongestLineLength.get(oldProvider) = m_consoleEditor.get(oldProvider).GetLongestLineLength(); + m_breakpoints.get(oldProvider) = m_textEditor.get(oldProvider).GetBreakpoints(); + m_cursorNeedsUpdate.get(oldProvider) = false; + m_consoleCursorNeedsUpdate.get(oldProvider) = false; } if (newProvider != nullptr) { - m_textEditor.SetText(m_sourceCode.get(newProvider)); - m_textEditor.SetCursorPosition(m_cursorPosition.get(newProvider)); + m_textEditor.get(newProvider).SetText(m_sourceCode.get(newProvider)); + m_textEditor.get(newProvider).SetCursorPosition(m_cursorPosition.get(newProvider)); TextEditor::Selection selection = m_selection.get(newProvider); - m_textEditor.SetSelection(selection.mStart, selection.mEnd); - m_textEditor.SetBreakpoints(m_breakpoints.get(newProvider)); - m_consoleEditor.SetText(hex::combineStrings(m_console.get(newProvider), "\n")); - m_consoleEditor.SetCursorPosition(m_consoleCursorPosition.get(newProvider)); - m_consoleEditor.SetLongestLineLength(m_consoleLongestLineLength.get(newProvider)); + m_textEditor.get(newProvider).SetSelection(selection.mStart, selection.mEnd); + m_textEditor.get(newProvider).SetBreakpoints(m_breakpoints.get(newProvider)); + m_consoleEditor.get(newProvider).SetText(hex::combineStrings(m_console.get(newProvider), "\n")); + m_consoleEditor.get(newProvider).SetCursorPosition(m_consoleCursorPosition.get(newProvider)); + m_consoleEditor.get(newProvider).SetLongestLineLength(m_consoleLongestLineLength.get(newProvider)); selection = m_consoleSelection.get(newProvider); - m_consoleEditor.SetSelection(selection.mStart, selection.mEnd); + m_consoleEditor.get(newProvider).SetSelection(selection.mStart, selection.mEnd); + m_cursorNeedsUpdate.get(newProvider) = true; + m_consoleCursorNeedsUpdate.get(newProvider) = true; } else { - m_textEditor.SetText(""); - m_consoleEditor.SetText(""); - m_consoleEditor.SetLongestLineLength(0); + m_textEditor.get(newProvider).SetText(""); + m_consoleEditor.get(newProvider).SetText(""); + m_consoleEditor.get(newProvider).SetLongestLineLength(0); } - m_textEditor.SetTextChanged(false); + m_textEditor.get(newProvider).SetTextChanged(false); }); RequestAddVirtualFile::subscribe(this, [this](const std::fs::path &path, const std::vector &data, Region region) { @@ -2102,7 +2125,8 @@ namespace hex::plugin::builtin { } void ViewPatternEditor::appendEditorText(const std::string &text) { - m_textEditor.AppendLine(text); + auto provider = ImHexApi::Provider::get(); + m_textEditor.get(provider).AppendLine(text); m_triggerEvaluation = true; } @@ -2120,11 +2144,12 @@ namespace hex::plugin::builtin { } TextEditor *ViewPatternEditor::getEditorFromFocusedWindow() { + auto provider = ImHexApi::Provider::get(); if (m_focusedSubWindowName.contains(consoleView)) { - return &m_consoleEditor; + return &m_consoleEditor.get(provider); } if (m_focusedSubWindowName.contains(textEditorView)) { - return &m_textEditor; + return &m_textEditor.get(provider); } return nullptr; } @@ -2137,7 +2162,7 @@ namespace hex::plugin::builtin { /* Export Pattern */ ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.export", "hex.builtin.menu.file.export.pattern" }, ICON_VS_FILE_CODE, 7050, Shortcut::None, m_exportPatternFile, [this] { - return !wolv::util::trim(m_textEditor.GetText()).empty() && ImHexApi::Provider::isValid(); + return !wolv::util::trim(m_textEditor.get(ImHexApi::Provider::get()).GetText()).empty() && ImHexApi::Provider::isValid(); } ); @@ -2295,18 +2320,18 @@ namespace hex::plugin::builtin { .load = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) { const auto sourceCode = tar.readString(basePath); - m_sourceCode.set(provider, sourceCode); + m_sourceCode.get(provider) = sourceCode; if (provider == ImHexApi::Provider::get()) - m_textEditor.SetText(sourceCode); + m_textEditor.get(provider).SetText(sourceCode); - m_hasUnevaluatedChanges = true; + m_hasUnevaluatedChanges.get(provider) = true; return true; }, .store = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) { if (provider == ImHexApi::Provider::get()) - m_sourceCode.set(provider, m_textEditor.GetText()); + m_sourceCode.get(provider) = m_textEditor.get(provider).GetText(); const auto &sourceCode = m_sourceCode.get(provider); @@ -2394,12 +2419,12 @@ namespace hex::plugin::builtin { ShortcutManager::addShortcut(this, CTRLCMD + Keys::V + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.paste", [this] { if (m_focusedSubWindowName.contains(textEditorView)) - m_textEditor.Paste(); + m_textEditor.get(ImHexApi::Provider::get()).Paste(); }); ShortcutManager::addShortcut(this, CTRLCMD + Keys::X + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.cut", [this] { if (m_focusedSubWindowName.contains(textEditorView)) - m_textEditor.Cut(); + m_textEditor.get(ImHexApi::Provider::get()).Cut(); }); // ShortcutManager::addShortcut(this, SHIFT + Keys::Delete + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.cut", [this] { @@ -2408,7 +2433,7 @@ namespace hex::plugin::builtin { ShortcutManager::addShortcut(this, CTRLCMD + Keys::Z + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.undo", [this] { if (m_focusedSubWindowName.contains(textEditorView)) - m_textEditor.Undo(); + m_textEditor.get(ImHexApi::Provider::get()).Undo(); }); // ShortcutManager::addShortcut(this, ALT + Keys::Backspace + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.undo", [this] { @@ -2417,12 +2442,12 @@ namespace hex::plugin::builtin { ShortcutManager::addShortcut(this, Keys::Delete + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.delete", [this] { if (m_focusedSubWindowName.contains(textEditorView)) - m_textEditor.Delete(); + m_textEditor.get(ImHexApi::Provider::get()).Delete(); }); ShortcutManager::addShortcut(this, CTRLCMD + Keys::Y + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.redo", [this] { if (m_focusedSubWindowName.contains(textEditorView)) - m_textEditor.Redo(); + m_textEditor.get(ImHexApi::Provider::get()).Redo(); }); ShortcutManager::addShortcut(this, CTRLCMD + Keys::A + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_all", [this] { @@ -2492,22 +2517,22 @@ namespace hex::plugin::builtin { ShortcutManager::addShortcut(this, CTRLCMD + Keys::Delete + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.delete_word_right", [this] { if (m_focusedSubWindowName.contains(textEditorView)) - m_textEditor.DeleteWordRight(); + m_textEditor.get(ImHexApi::Provider::get()).DeleteWordRight(); }); ShortcutManager::addShortcut(this, CTRLCMD + Keys::Backspace + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.delete_word_left", [this] { if (m_focusedSubWindowName.contains(textEditorView)) - m_textEditor.DeleteWordLeft(); + m_textEditor.get(ImHexApi::Provider::get()).DeleteWordLeft(); }); ShortcutManager::addShortcut(this, Keys::Backspace + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.backspace", [this] { if (m_focusedSubWindowName.contains(textEditorView)) - m_textEditor.Backspace(); + m_textEditor.get(ImHexApi::Provider::get()).Backspace(); }); ShortcutManager::addShortcut(this, Keys::Insert + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.toggle_insert", [this] { if (m_focusedSubWindowName.contains(textEditorView)) - m_textEditor.SetOverwrite(!m_textEditor.IsOverwrite()); + m_textEditor.get(ImHexApi::Provider::get()).SetOverwrite(!m_textEditor.get(ImHexApi::Provider::get()).IsOverwrite()); }); ShortcutManager::addShortcut(this, CTRLCMD + Keys::Right + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_word_right", [this] { @@ -2581,11 +2606,11 @@ namespace hex::plugin::builtin { }); ShortcutManager::addShortcut(this, Keys::F8 + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.add_breakpoint", [this] { - const auto line = m_textEditor.GetCursorPosition().mLine + 1; + const auto line = m_textEditor.get(ImHexApi::Provider::get()).GetCursorPosition().mLine + 1; const auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); auto &evaluator = runtime.getInternals().evaluator; - m_breakpoints = m_textEditor.GetBreakpoints(); + m_breakpoints = m_textEditor.get(ImHexApi::Provider::get()).GetBreakpoints(); evaluator->setBreakpoints(m_breakpoints); if (m_breakpoints->contains(line)) { @@ -2594,7 +2619,7 @@ namespace hex::plugin::builtin { evaluator->addBreakpoint(line); } m_breakpoints = evaluator->getBreakpoints(); - m_textEditor.SetBreakpoints(m_breakpoints); + m_textEditor.get(ImHexApi::Provider::get()).SetBreakpoints(m_breakpoints); }); /* Trigger evaluation */