diff --git a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h index 304b45c21..67a0dc1bd 100644 --- a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h +++ b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h @@ -137,6 +137,8 @@ namespace ImGuiExt { void UnderlinedText(const char *label, ImColor color = ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg = ImVec2(0, 0)); + void UnderwavedText(const char *label, ImColor textColor = ImGui::GetStyleColorVec4(ImGuiCol_Text), ImColor lineColor = ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg = ImVec2(0, 0)); + void TextSpinner(const char *label); void Header(const char *label, bool firstEntry = false); diff --git a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp index 5d4d6a589..6b54eae28 100644 --- a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp +++ b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp @@ -550,6 +550,34 @@ namespace ImGuiExt { PopStyleColor(); } + void UnderwavedText(const char *label, ImColor textColor, ImColor lineColor, const ImVec2 &size_arg) { + ImGuiWindow *window = GetCurrentWindow(); + std::string labelStr(label); + for (char letter : labelStr) { + std::string letterStr(1, letter); + const ImVec2 label_size = CalcTextSize(letterStr.c_str(), nullptr, true); + ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y); + ImVec2 pos = window->DC.CursorPos; + float lineWidth = size.x / 3.0f; + float halfLineW = lineWidth / 2.0f; + float lineY = pos.y + size.y; + ImVec2 initial = ImVec2(pos.x, lineY); + ImVec2 pos1 = ImVec2(pos.x + lineWidth, lineY - 2.0f); + ImVec2 pos2 = ImVec2(pos.x + lineWidth + halfLineW, lineY); + ImVec2 pos3 = ImVec2(pos.x + lineWidth * 2 + halfLineW, lineY - 2.0f); + ImVec2 pos4 = ImVec2(pos.x + lineWidth * 3, lineY - 1.0f); + + PushStyleColor(ImGuiCol_Text, ImU32(textColor)); + TextEx(letterStr.c_str(), nullptr, ImGuiTextFlags_NoWidthForLargeClippedText); // Skip formatting + GetWindowDrawList()->AddLine(initial, pos1, ImU32(lineColor),0.4f); + GetWindowDrawList()->AddLine(pos1, pos2, ImU32(lineColor),0.3f); + GetWindowDrawList()->AddLine(pos2, pos3, ImU32(lineColor),0.4f); + GetWindowDrawList()->AddLine(pos3, pos4, ImU32(lineColor),0.3f); + PopStyleColor(); + window->DC.CursorPos = ImVec2(pos.x + size.x, pos.y); + } + } + void TextSpinner(const char *label) { Text("[%c] %s", "|/-\\"[ImU32(GetTime() * 20) % 4], label); } diff --git a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h index f6172f080..ebe3784e4 100644 --- a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h +++ b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h @@ -128,13 +128,14 @@ public: std::string mDeclaration; }; - typedef std::string String; - typedef std::unordered_map Identifiers; - typedef std::unordered_set Keywords; - typedef std::map ErrorMarkers; - typedef std::unordered_set Breakpoints; - typedef std::array Palette; - typedef uint8_t Char; + using String = std::string; + using Identifiers = std::unordered_map; + using Keywords = std::unordered_set ; + using ErrorMarkers = std::map>; + using ErrorHoverBoxes = std::map>; + using Breakpoints = std::unordered_set; + using Palette = std::array; + using Char = uint8_t ; struct Glyph { @@ -199,6 +200,7 @@ public: void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; } void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers; } + 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); @@ -473,6 +475,7 @@ private: bool mCheckComments; Breakpoints mBreakpoints; ErrorMarkers mErrorMarkers; + ErrorHoverBoxes mErrorHoverBoxes; ImVec2 mCharAdvance; Coordinates mInteractiveStart, mInteractiveEnd; std::string mLineBuffer; diff --git a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp index 0d3c714e2..8c540a4e4 100644 --- a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp +++ b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp @@ -38,6 +38,37 @@ TextEditor::TextEditor() TextEditor::~TextEditor() { } + +ImVec2 TextEditor::Underwaves( ImVec2 pos ,uint32_t nChars, ImColor color, const ImVec2 &size_arg) { + auto save = ImGui::GetStyle().AntiAliasedLines; + ImGui::GetStyle().AntiAliasedLines = false; + ImGuiWindow *window = ImGui::GetCurrentWindow(); + window->DC.CursorPos =pos; + const ImVec2 label_size = ImGui::CalcTextSize("W", nullptr, true); + ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x, label_size.y); + float lineWidth = size.x / 3.0f + 0.5f; + float halfLineW = lineWidth / 2.0f; + + for (uint32_t i = 0; i < nChars; i++) { + pos = window->DC.CursorPos; + float lineY = pos.y + size.y; + + ImVec2 pos1_1 = ImVec2(pos.x + 0*lineWidth, lineY + halfLineW); + ImVec2 pos1_2 = ImVec2(pos.x + 1*lineWidth, lineY - halfLineW); + ImVec2 pos2_1 = ImVec2(pos.x + 2*lineWidth, lineY + halfLineW); + ImVec2 pos2_2 = ImVec2(pos.x + 3*lineWidth, lineY - halfLineW); + + ImGui::GetWindowDrawList()->AddLine(pos1_1, pos1_2, ImU32(color), 0.4f); + ImGui::GetWindowDrawList()->AddLine(pos1_2, pos2_1, ImU32(color), 0.4f); + ImGui::GetWindowDrawList()->AddLine(pos2_1, pos2_2, ImU32(color), 0.4f); + + window->DC.CursorPos = ImVec2(pos.x + size.x, pos.y); + } + auto ret = window->DC.CursorPos; + ret.y += size.y; + return ret; +} + void TextEditor::SetLanguageDefinition(const LanguageDefinition &aLanguageDef) { mLanguageDefinition = aLanguageDef; mRegexList.clear(); @@ -541,8 +572,8 @@ void TextEditor::RemoveLine(int aStart, int aEnd) { 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) + ErrorMarkers::value_type e(i.first.mLine >= aStart ? Coordinates(i.first.mLine - 1,i.first.mColumn ) : i.first, i.second); + if (e.first.mLine >= aStart && e.first.mLine <= aEnd) continue; etmp.insert(e); } @@ -555,9 +586,10 @@ void TextEditor::RemoveLine(int aStart, int aEnd) { btmp.insert(i >= aStart ? i - 1 : i); } mBreakpoints = std::move(btmp); - - mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd); - assert(!mLines.empty()); + if (aStart == 0 && aEnd == (int32_t)mLines.size() - 1) + mLines.erase(mLines.begin() + aStart, mLines.end()); + else + mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd + 1); mTextChanged = true; } @@ -568,8 +600,8 @@ void TextEditor::RemoveLine(int aIndex) { 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) + ErrorMarkers::value_type e(i.first.mLine > aIndex ? Coordinates(i.first.mLine - 1 ,i.first.mColumn) : i.first, i.second); + if (e.first.mLine - 1 == aIndex) continue; etmp.insert(e); } @@ -594,7 +626,7 @@ TextEditor::Line &TextEditor::InsertLine(int aIndex) { ErrorMarkers etmp; for (auto &i : mErrorMarkers) - etmp.insert(ErrorMarkers::value_type(i.first >= aIndex ? i.first + 1 : i.first, i.second)); + etmp.insert(ErrorMarkers::value_type(i.first.mLine >= aIndex ? Coordinates(i.first.mLine + 1,i.first.mColumn) : i.first, i.second)); mErrorMarkers = std::move(etmp); Breakpoints btmp; @@ -853,25 +885,6 @@ void TextEditor::Render() { auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y); - // 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(0.5f, 0.5f, 0.2f, 1.0f)); - ImGui::Text("%s", errorIt->second.c_str()); - ImGui::PopStyleColor(); - ImGui::EndTooltip(); - } - } - // Draw line number (right aligned) if (mShowLineNumbers) { snprintf(buf, 16, "%d ", lineNo + 1); @@ -891,7 +904,6 @@ void TextEditor::Render() { if (mState.mCursorPosition.mLine == lineNo && mShowCursor) { bool focused = ImGui::IsWindowFocused(); - ImGuiViewport *viewport = ImGui::GetWindowViewport(); // Highlight the current line (where the cursor is) if (!HasSelection()) { @@ -937,7 +949,14 @@ void TextEditor::Render() { for (int i = 0; i < line.size();) { auto &glyph = line[i]; auto color = GetGlyphColor(glyph); - + bool underwaved = false; + ErrorMarkers::iterator errorIt; + if (mErrorMarkers.size() > 0) { + errorIt = mErrorMarkers.find(Coordinates(lineNo+1,i)); + if (errorIt != mErrorMarkers.end()) { + underwaved = true; + } + } 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()); @@ -945,6 +964,13 @@ void TextEditor::Render() { bufferOffset.x += textSize.x; mLineBuffer.clear(); } + if (underwaved) { + auto textStart = TextDistanceToLineStart(Coordinates(lineNo, i-1)) + mTextStart; + auto begin = ImVec2(lineStartScreenPos.x + textStart, lineStartScreenPos.y); + auto end = Underwaves(begin, errorIt->second.first, mPalette[(int32_t) PaletteIndex::ErrorMarker]); + mErrorHoverBoxes[Coordinates(lineNo+1,i)]=std::make_pair(begin,end); + } + prevColor = color; if (glyph.mChar == '\t') { @@ -1021,6 +1047,22 @@ void TextEditor::Render() { mScrollToCursor = false; } + for (auto [key,value] : mErrorMarkers) { + auto start = mErrorHoverBoxes[key].first; + auto end = mErrorHoverBoxes[key].second; + if (ImGui::IsMouseHoveringRect(start, end)) { + ImGui::BeginTooltip(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f)); + ImGui::Text("Error at line %d:", key.mLine); + ImGui::PopStyleColor(); + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.2f, 1.0f)); + ImGui::Text("%s", value.second.c_str()); + ImGui::PopStyleColor(); + ImGui::EndTooltip(); + } + } + ImGuiPopupFlags_ popup_flags = ImGuiPopupFlags_None; ImGuiContext& g = *GImGui; auto oldTopMargin = mTopMargin; @@ -1788,7 +1830,7 @@ void TextEditor::Backspace() { ErrorMarkers etmp; for (auto &i : mErrorMarkers) - etmp.insert(ErrorMarkers::value_type(i.first - 1 == mState.mCursorPosition.mLine ? i.first - 1 : i.first, i.second)); + etmp.insert(ErrorMarkers::value_type(i.first.mLine - 1 == mState.mCursorPosition.mLine ? Coordinates(i.first.mLine - 1,i.first.mColumn) : i.first, i.second)); mErrorMarkers = std::move(etmp); RemoveLine(mState.mCursorPosition.mLine); @@ -2457,7 +2499,7 @@ void TextEditor::ColorizeRange(int aFromLine, int aToLine) { hasTokenizeResult = true; } - if (hasTokenizeResult == false) { + if (!hasTokenizeResult) { // todo : remove // printf("using regex for %.*s\n", first + 10 < last ? 10 : int(last - first), first); diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index f32b04646..db25fe728 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -250,6 +250,7 @@ namespace hex::plugin::builtin { PerProvider> m_lastEvaluationError; PerProvider> m_lastCompileError; + PerProvider> m_callStack; PerProvider> m_lastEvaluationOutVars; PerProvider> m_patternVariables; PerProvider> m_sections; diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index b0b086770..4435b9d44 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -1343,15 +1343,27 @@ namespace hex::plugin::builtin { }; TextEditor::ErrorMarkers errorMarkers; - if (m_lastEvaluationError->has_value()) { - errorMarkers[(*m_lastEvaluationError)->line] = processMessage((*m_lastEvaluationError)->message); + if (!m_callStack->empty()) { + for (const auto &frame : *m_callStack | std::views::reverse) { + auto location = frame->getLocation(); + std::string message; + if (location.source->source == pl::api::Source::DefaultSource) { + if (m_lastEvaluationError->has_value()) + message = processMessage((*m_lastEvaluationError)->message); + auto key = TextEditor::Coordinates(location.line, location.column); + errorMarkers[key] = std::make_pair(location.length, message); + } + } } if (!m_lastCompileError->empty()) { for (const auto &error : *m_lastCompileError) { - auto source = error.getLocation().source; - if (source != nullptr && source->source == pl::api::Source::DefaultSource) - errorMarkers[error.getLocation().line] = processMessage(error.getMessage()); + auto source = error.getLocation().source; + if (source != nullptr && source->source == pl::api::Source::DefaultSource) { + auto key = TextEditor::Coordinates(error.getLocation().line, error.getLocation().column); + if (!errorMarkers.contains(key) ||errorMarkers[key].first < error.getLocation().length) + errorMarkers[key] = std::make_pair(error.getLocation().length,processMessage(error.getMessage())); + } } } @@ -1794,6 +1806,7 @@ namespace hex::plugin::builtin { if (!m_lastEvaluationResult) { *m_lastEvaluationError = runtime.getEvalError(); *m_lastCompileError = runtime.getCompileErrors(); + *m_callStack = reinterpret_cast &>(runtime.getInternals().evaluator->getCallStack()); } TaskManager::doLater([code] {