diff --git a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_decoded_string.hpp b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_decoded_string.hpp index 9da2db17c..a1ca04be7 100644 --- a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_decoded_string.hpp +++ b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_decoded_string.hpp @@ -3,7 +3,6 @@ #include #include -#include #include namespace hex::plugin::builtin { @@ -21,6 +20,5 @@ namespace hex::plugin::builtin { private: std::string m_decodedString; - ui::TextEditor m_editor; }; } diff --git a/plugins/builtin/include/content/views/view_bookmarks.hpp b/plugins/builtin/include/content/views/view_bookmarks.hpp index d664b462b..aecbb383d 100644 --- a/plugins/builtin/include/content/views/view_bookmarks.hpp +++ b/plugins/builtin/include/content/views/view_bookmarks.hpp @@ -3,8 +3,6 @@ #include #include -#include - #include #include diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index 57eca8584..51b4ad54c 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -156,8 +156,8 @@ namespace hex::plugin::builtin { PerProvider m_consoleCursorPosition; PerProvider m_cursorNeedsUpdate; PerProvider m_consoleCursorNeedsUpdate; - PerProvider m_selection; - PerProvider m_consoleSelection; + PerProvider m_selection; + PerProvider m_consoleSelection; PerProvider m_consoleLongestLineLength; PerProvider m_breakpoints; PerProvider> m_lastEvaluationError; diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_decoded_string.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_decoded_string.cpp index f46771265..ecce598e0 100644 --- a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_decoded_string.cpp +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_decoded_string.cpp @@ -8,8 +8,6 @@ #include -#include - namespace hex::plugin::builtin { PopupDecodedString::PopupDecodedString(std::string decodedString) : m_decodedString(std::move(decodedString)) { diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_fill.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_fill.cpp index a71ceaab5..bf45563c5 100644 --- a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_fill.cpp +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_fill.cpp @@ -13,7 +13,6 @@ #include -#include #include using namespace wolv::literals; diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_find.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_find.cpp index 0dd09e4de..9cdf25a54 100644 --- a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_find.cpp +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_find.cpp @@ -18,8 +18,6 @@ #include -#include - namespace hex::plugin::builtin { PerProvider PopupFind::s_inputString; diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_paste_behaviour.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_paste_behaviour.cpp index 0f2852fdd..a4098e308 100644 --- a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_paste_behaviour.cpp +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_paste_behaviour.cpp @@ -3,8 +3,6 @@ #include -#include - namespace hex::plugin::builtin { PopupPasteBehaviour::PopupPasteBehaviour(const Region &selection, const std::function &pasteCallback) diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_remove.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_remove.cpp index 2155e8c07..a397c32b3 100644 --- a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_remove.cpp +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_remove.cpp @@ -6,8 +6,6 @@ #include -#include - namespace hex::plugin::builtin { PopupRemove::PopupRemove(u64 address, size_t size) : m_address(address), m_size(size) {} diff --git a/plugins/builtin/source/content/text_highlighting/pattern_language.cpp b/plugins/builtin/source/content/text_highlighting/pattern_language.cpp index 50cb2aeca..7b0737f45 100644 --- a/plugins/builtin/source/content/text_highlighting/pattern_language.cpp +++ b/plugins/builtin/source/content/text_highlighting/pattern_language.cpp @@ -1247,7 +1247,7 @@ namespace hex::plugin::builtin { if (isLocationValid(error.getLocation())) { auto key = ui::TextEditor::Coordinates(error.getLocation().line, error.getLocation().column); - if (!errorMarkers.contains(key) || errorMarkers[key].first < error.getLocation().length) + if (!errorMarkers.contains(key) || errorMarkers[key].first < (i32) error.getLocation().length) errorMarkers[key] = std::make_pair(error.getLocation().length, processMessage(error.getMessage())); } } diff --git a/plugins/builtin/source/content/views/view_bookmarks.cpp b/plugins/builtin/source/content/views/view_bookmarks.cpp index 48d844d55..52a727edb 100644 --- a/plugins/builtin/source/content/views/view_bookmarks.cpp +++ b/plugins/builtin/source/content/views/view_bookmarks.cpp @@ -20,6 +20,7 @@ #include #include +#include "imgui_internal.h" namespace hex::plugin::builtin { diff --git a/plugins/builtin/source/content/views/view_hex_editor.cpp b/plugins/builtin/source/content/views/view_hex_editor.cpp index 3323a5b90..fbb54aab8 100644 --- a/plugins/builtin/source/content/views/view_hex_editor.cpp +++ b/plugins/builtin/source/content/views/view_hex_editor.cpp @@ -43,7 +43,6 @@ #include #include #include -#include #include using namespace std::literals::string_literals; diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index ae40c6d8d..5db652450 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -1352,7 +1352,7 @@ namespace hex::plugin::builtin { auto source = error.getLocation().source; if (source != nullptr && source->mainSource) { auto key = ui::TextEditor::Coordinates(error.getLocation().line, error.getLocation().column); - if (!errorMarkers.contains(key) ||errorMarkers[key].first < error.getLocation().length) + if (!errorMarkers.contains(key) || (u32) errorMarkers[key].first < error.getLocation().length) errorMarkers[key] = std::make_pair(u32(error.getLocation().length), processMessage(error.getMessage())); } } @@ -1849,7 +1849,7 @@ namespace hex::plugin::builtin { if (newProvider != nullptr) { m_textEditor.get(newProvider).setText(wolv::util::preprocessText(m_sourceCode.get(newProvider))); m_textEditor.get(newProvider).setCursorPosition(m_cursorPosition.get(newProvider)); - ui::TextEditor::Selection selection = m_selection.get(newProvider); + ui::TextEditor::Range selection = m_selection.get(newProvider); m_textEditor.get(newProvider).setSelection(selection); m_textEditor.get(newProvider).setBreakpoints(m_breakpoints.get(newProvider)); m_consoleEditor.get(newProvider).setText(wolv::util::combineStrings(m_console.get(newProvider), "\n")); @@ -2089,7 +2089,7 @@ namespace hex::plugin::builtin { /* Add Breakpoint */ ContentRegistry::UserInterface::addMenuItem({ "hex.builtin.menu.edit", "hex.builtin.view.pattern_editor.menu.edit.add_breakpoint"}, ICON_VS_DEBUG_BREAKPOINT_DATA, 1750, Keys::F8 + AllowWhileTyping, [this] { - const auto line = m_textEditor.get(ImHexApi::Provider::get()).getCursorPosition().m_line + 1; + const auto line = m_textEditor.get(ImHexApi::Provider::get()).getCursorPosition().getLine() + 1; const auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); auto &evaluator = runtime.getInternals().evaluator; diff --git a/plugins/ui/include/ui/text_editor.hpp b/plugins/ui/include/ui/text_editor.hpp index afb11d293..bb00c0a7a 100644 --- a/plugins/ui/include/ui/text_editor.hpp +++ b/plugins/ui/include/ui/text_editor.hpp @@ -14,68 +14,110 @@ #include "imgui.h" #include "imgui_internal.h" #include +#include namespace hex::ui { using strConstIter = std::string::const_iterator; class TextEditor { - public: - // indices of the arrays that contain the lines (vector) and the columns (a string) of the - // text editor. Negative values indicate the distance to the last element of the array. - // When comparing coordinates ensure they have the same sign because coordinates don't have - // information about the size of the array. Currently positive coordinates are always bigger - // than negatives even if that gives a wrong result. - struct Coordinates { - i32 m_line, m_column; - Coordinates() : m_line(0), m_column(0) {} - Coordinates(i32 line, i32 column) : m_line(line), m_column(column) {} - bool operator==(const Coordinates &o) const; - bool operator!=(const Coordinates &o) const; - bool operator<(const Coordinates &o) const; - bool operator>(const Coordinates &o) const; - bool operator<=(const Coordinates &o) const; - bool operator>=(const Coordinates &o) const; - Coordinates operator+(const Coordinates &o) const; - Coordinates operator-(const Coordinates &o) const; - }; + class Range { + public: + friend class TextEditor; + friend class Lines; - inline static const Coordinates Invalid = Coordinates(0x80000000, 0x80000000); + // Coordinates represent 2-dimensional points used to identify locations in the pattern editor. + // as line number and column number pairs. Coordinates can be folded and unfolded. Folded + // lines are called rows. Columns keep their name. You can convert lines to rows and vice versa or + // the supplied functions to convert whole coordinates. Unfolded and folded coordinates come in two types. + // The first one referred to as (plain) coordinates correspond to the line number for the y component + // and the utf8 character index within the line for the x coordinate. The second kind are given the + // name of Index coordinate or just plain index. These correspond directly + // to the indices of the c++ containers holding the editor data. Negative values are used to index + // from the end of the respective c++ container. In any document with N lines and M_N columns on each line + // first character can be described equally by the Coordinates (0,0) or (-M, -N_0) and the last one as either + // (M-1, N_(M-1)-1) or just (-1,-1). + class Coordinates { + public: + friend class TextEditor; + friend class ViewPatternEditor; + Coordinates() : m_line(0), m_column(0) {} + explicit Coordinates(pl::core::Location location) : m_line(location.line - 1), m_column(location.column - 1) {} + Coordinates(i32 lineIndex, i32 column) : m_line(lineIndex), m_column(column) {} + Coordinates(TextEditor *editor, i32 lineIndex, i32 column); + Coordinates sanitize(TextEditor *editor); + bool isValid(TextEditor *editor) const; + bool operator==(const Coordinates &o) const; + bool operator!=(const Coordinates &o) const; + bool operator<(const Coordinates &o) const; + bool operator>(const Coordinates &o) const; + bool operator<=(const Coordinates &o) const; + bool operator>=(const Coordinates &o) const; + Coordinates operator+(const Coordinates &o) const; + Coordinates operator-(const Coordinates &o) const; + i32 getLine() const { return m_line; } + i32 getColumn() const { return m_column; } - struct Selection { - Coordinates m_start; - Coordinates m_end; + private: + i32 m_line, m_column; + }; + + Range() = default; + explicit Range(std::pair coords) : m_start(coords.first), m_end(coords.second) { + if (m_start > m_end) { std::swap(m_start, m_end); }} + Range(Coordinates start, Coordinates end) : m_start(start), m_end(end) { + if (m_start > m_end) { std::swap(m_start, m_end); }} - Selection() = default; - Selection(Coordinates start, Coordinates end) : m_start(start), m_end(end) { - if (m_start > m_end) { - std::swap(m_start, m_end); - } - } Coordinates getSelectedLines(); Coordinates getSelectedColumns(); + Coordinates getStart() { return m_start; } + Coordinates getEnd() { return m_end; } bool isSingleLine(); - bool contains(Coordinates coordinates, int8_t endsInclusive=1); - bool operator==(const Selection &o) const { - return m_start == o.m_start && m_end == o.m_end; + enum class EndsInclusive : u8 { None = 0, Start = 2, End = 1, Both = 3 }; + bool contains(const Coordinates &coordinates, EndsInclusive endsInclusive = EndsInclusive::Both) const; + bool contains(const Range &range, EndsInclusive endsInclusive = EndsInclusive::Both) const; + bool containsLine(i32 value, EndsInclusive endsInclusive = EndsInclusive::Both) const; + bool containsColumn(i32 value, EndsInclusive endsInclusive = EndsInclusive::Both) const; + bool overlaps(const Range &o, EndsInclusive endsInclusive = EndsInclusive::Both) const; + bool operator==(const Range &o) const; + bool operator!=(const Range &o) const; + bool operator<(const Range &o) const { + return m_end < o.m_end; } - bool operator!=(const Selection &o) const { - return m_start != o.m_start || m_end != o.m_end; + bool operator>(const Range &o) const { + return m_end > o.m_end; } + bool operator<=(const Range &o) const { + return !(*this > o); + } + bool operator>=(const Range &o) const { + return !(*this < o); + } + + private: + Coordinates m_start; + Coordinates m_end; }; - struct EditorState { - Selection m_selection; + using Coordinates = Range::Coordinates; + class EditorState { + public: + friend class TextEditor; + bool operator==(const EditorState &o) const; + EditorState() : m_selection(), m_cursorPosition() {} + EditorState(const Range &selection, const Coordinates &cursorPosition) : m_selection(selection), m_cursorPosition(cursorPosition) {} + private: + Range m_selection; Coordinates m_cursorPosition; }; class FindReplaceHandler { public: FindReplaceHandler(); - typedef std::vector Matches; + using Matches = std::vector; Matches &getMatches() { return m_matches; } bool findNext(TextEditor *editor); u32 findMatch(TextEditor *editor, i32 index); @@ -139,153 +181,112 @@ namespace hex::ui { }; enum class PaletteIndex { - Default, - Identifier, - Directive, - Operator, - Separator, - BuiltInType, - Keyword, - NumericLiteral, - StringLiteral, - CharLiteral, - Cursor, - Background, - LineNumber, - Selection, - Breakpoint, - ErrorMarker, - PreprocessorDeactivated, - CurrentLineFill, - CurrentLineFillInactive, - CurrentLineEdge, - ErrorText, - WarningText, - DebugText, - DefaultText, - Attribute, - PatternVariable, - LocalVariable, - CalculatedPointer, - TemplateArgument, - Function, - View, - FunctionVariable, - FunctionParameter, - UserDefinedType, - PlacedVariable, - GlobalVariable, - NameSpace, - TypeDef, - UnkIdentifier, - DocComment, - DocBlockComment, - BlockComment, - GlobalDocComment, - Comment, - PreprocIdentifier, - Max + Default, Identifier, Directive, Operator, Separator, BuiltInType, Keyword, NumericLiteral, StringLiteral, CharLiteral, Cursor, Background, LineNumber, Selection, Breakpoint, ErrorMarker, PreprocessorDeactivated, + CurrentLineFill, CurrentLineFillInactive, CurrentLineEdge, ErrorText, WarningText, DebugText, DefaultText, Attribute, PatternVariable, LocalVariable, CalculatedPointer, TemplateArgument, Function, View, + FunctionVariable, FunctionParameter, UserDefinedType, PlacedVariable, GlobalVariable, NameSpace, TypeDef, UnkIdentifier, DocComment, DocBlockComment, BlockComment, GlobalDocComment, Comment, PreprocIdentifier, Max }; + using RegexList = std::vector>; + using Keywords = std::unordered_set; + using ErrorMarkers = std::map>; + using Breakpoints = std::unordered_set; + using Palette = std::array; + using Glyph = u8; - typedef std::vector> RegexList; - - struct Identifier { - Coordinates m_location; - std::string m_declaration; - }; - - using String = std::string; - using Identifiers = std::unordered_map; - using Keywords = std::unordered_set; - using ErrorMarkers = std::map>; - using Breakpoints = std::unordered_set; - using Palette = std::array; - using Glyph = uint8_t; + struct Identifier { Coordinates m_location; std::string m_declaration;}; + using Identifiers = std::unordered_map; class ActionableBox { - - ImRect m_box; public: - ActionableBox() = default; + ActionableBox() : m_box(ImRect(ImVec2(0, 0), ImVec2(0, 0))) {}; explicit ActionableBox(const ImRect &box) : m_box(box) {} - virtual bool trigger() { - return ImGui::IsMouseHoveringRect(m_box.Min, m_box.Max); - } - + ImRect &getBox() const { return const_cast(m_box);} + virtual bool trigger(); virtual void callback() {} + virtual ~ActionableBox() = default; + void shiftBoxVertically(float lineCount, float lineHeight); + private: + ImRect m_box; }; class CursorChangeBox : public ActionableBox { public: - CursorChangeBox() = default; + CursorChangeBox() : ActionableBox(ImRect(ImVec2(0, 0), ImVec2(0, 0))) {} explicit CursorChangeBox(const ImRect &box) : ActionableBox(box) {} - void callback() override { - ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); - } + void callback() override { ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);} }; class ErrorGotoBox : public ActionableBox { - Coordinates m_pos; public: ErrorGotoBox() = default; - ErrorGotoBox(const ImRect &box, const Coordinates &pos, TextEditor *editor) : ActionableBox(box), m_pos(pos), m_editor(editor) {} - bool trigger() override { - return ActionableBox::trigger() && ImGui::IsMouseClicked(0); - } - - void callback() override { - m_editor->jumpToCoords(m_pos); - } - + bool trigger() override { return ActionableBox::trigger() && ImGui::IsMouseClicked(0);} + void callback() override { m_editor->jumpToCoords(m_pos);} private: + Coordinates m_pos; TextEditor *m_editor = nullptr; }; - using ErrorGotoBoxes = std::map; - using CursorBoxes = std::map; - class ErrorHoverBox : public ActionableBox { - Coordinates m_pos; - std::string m_errorText; public: ErrorHoverBox() = default; ErrorHoverBox(const ImRect &box, const Coordinates &pos, const char *errorText) : ActionableBox(box), m_pos(pos), m_errorText(errorText) {} - void callback() override { - ImGui::BeginTooltip(); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f)); - ImGui::Text("Error at line %d:", m_pos.m_line); - ImGui::PopStyleColor(); - ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.2f, 1.0f)); - ImGui::TextUnformatted(m_errorText.c_str()); - ImGui::PopStyleColor(); - ImGui::EndTooltip(); - } + void callback() override; + private: + Coordinates m_pos; + std::string m_errorText; }; - using ErrorHoverBoxes = std::map; + using ErrorGotoBoxes = std::map; + using CursorBoxes = std::map; + using ErrorHoverBoxes = std::map; + enum class TrimMode : u8 { TrimNone = 0, TrimEnd = 1, TrimStart = 2, TrimBoth = 3 }; // A line of text in the pattern editor consists of three strings; the character encoding, the color encoding and the flags. // The char encoding is utf-8, the color encoding are indices to the color palette and the flags are used to override the colors // depending on priorities; e.g. comments, strings, etc. + class LineIterator { + public: + friend class hex::ui::TextEditor; + LineIterator(const LineIterator &other) : m_charsIter(other.m_charsIter), m_colorsIter(other.m_colorsIter), m_flagsIter(other.m_flagsIter) {} + LineIterator() = default; + + char operator*(); + LineIterator operator++(); + LineIterator operator=(const LineIterator &other); + bool operator!=(const LineIterator &other) const; + bool operator==(const LineIterator &other) const; + LineIterator operator+(i32 n); + i32 operator-(LineIterator l); + private: + strConstIter m_charsIter; + strConstIter m_colorsIter; + strConstIter m_flagsIter; + }; + class Line { public: + friend class TextEditor; + enum class Comments : u8 { + NoComment = 0, + Doc = 0b0001, + Block = 0b0010, + BlockDoc = 0b0011, + Line = 0b0100, + Global = 0b0101, + }; struct FlagBits { - bool comment: 1; - bool blockComment: 1; - bool docComment: 1; - bool blockDocComment: 1; - bool globalDocComment: 1; + bool doc: 1; + bool block: 1; + bool global: 1; bool deactivated: 1; bool preprocessor: 1; - bool matchedBracket: 1; + bool matchedDelimiter: 1; }; union Flags { @@ -295,64 +296,40 @@ namespace hex::ui { char m_value; }; - constexpr static char inComment = 31; + enum class LinePart { Chars, Utf8, Colors, Flags }; - class LineIterator { - public: - strConstIter m_charsIter; - strConstIter m_colorsIter; - strConstIter m_flagsIter; - - LineIterator(const LineIterator &other) : m_charsIter(other.m_charsIter), m_colorsIter(other.m_colorsIter), m_flagsIter(other.m_flagsIter) {} - - LineIterator() = default; - - char operator*(); - LineIterator operator++(); - LineIterator operator=(const LineIterator &other); - bool operator!=(const LineIterator &other) const; - bool operator==(const LineIterator &other) const; - LineIterator operator+(i32 n); - i32 operator-(LineIterator l); - }; - - enum class LinePart { - Chars, - Utf8, - Colors, - Flags - }; + Line() : m_chars(""), m_colors(""), m_flags(""), m_colorized(false), m_lineMaxColumn(-1) {} + explicit Line(const char *line) { Line(std::string(line)); } + explicit Line(const std::string &line) : m_chars(line), m_colors(std::string(line.size(), 0x00)), m_flags(std::string(line.size(), 0x00)), m_colorized(false), m_lineMaxColumn(maxColumn()) {} + Line(const Line &line) : m_chars(std::string(line.m_chars)), m_colors(std::string(line.m_colors)), m_flags(std::string(line.m_flags)), m_colorized(line.m_colorized), m_lineMaxColumn(line.m_lineMaxColumn) {} + Line(Line &&line) noexcept : m_chars(std::move(line.m_chars)), m_colors(std::move(line.m_colors)), m_flags(std::move(line.m_flags)), m_colorized(line.m_colorized), m_lineMaxColumn(line.m_lineMaxColumn) {} + Line(std::string chars, std::string colors, std::string flags) : m_chars(std::move(chars)), m_colors(std::move(colors)), m_flags(std::move(flags)), m_colorized(false), m_lineMaxColumn(maxColumn()) {} + bool operator==(const Line &o) const; + bool operator!=(const Line &o) const; + i32 indexColumn(i32 stringIndex) const; + i32 maxColumn(); + i32 maxColumn() const; + i32 columnIndex(i32 column) const; + i32 textSize() const; + i32 textSize(u32 index) const; + i32 lineTextSize(TrimMode trimMode = TrimMode::TrimNone); + i32 stringTextSize(const std::string &str) const; + i32 textSizeIndex(float textSize, i32 position); LineIterator begin() const; LineIterator end() const; - - std::string m_chars; - std::string m_colors; - std::string m_flags; - bool m_colorized = false; - i32 m_lineTextSize; - - Line() : m_chars(), m_colors(), m_flags(), m_colorized(false), m_lineTextSize(-1) {} - explicit Line(const char *line) { Line(std::string(line)); } - explicit Line(const std::string &line) : - m_chars(line), m_colors(std::string(line.size(), 0x00)), m_flags(std::string(line.size(), 0x00)), m_colorized(false), m_lineTextSize( - getLineTextSize()) {} - Line(const Line &line) : m_chars(line.m_chars), m_colors(line.m_colors), m_flags(line.m_flags), m_colorized(line.m_colorized), m_lineTextSize(line.m_lineTextSize) {} - - i32 getCharColumn(i32 stringIndex) const; - i32 getColumnIndex(i32 column) const; - i32 getLineTextSize(); - i32 getStringTextSize(const std::string &str) const; LineIterator begin(); LineIterator end(); Line &operator=(const Line &line); Line &operator=(Line &&line) noexcept; u64 size() const; + TextEditor::Line trim(TrimMode trimMode); char front(LinePart part = LinePart::Chars) const; std::string frontUtf8(LinePart part = LinePart::Chars) const; void push_back(char c); bool empty() const; std::string substr(u64 start, u64 length = (u64) -1, LinePart part = LinePart::Chars) const; + Line subLine(u64 start, u64 length = (u64) -1); char operator[](u64 index) const; // C++ can't overload functions based on return type, so use any type other // than u64 to avoid ambiguity. @@ -370,11 +347,18 @@ namespace hex::ui { void insert(LineIterator iter, LineIterator beginLine, LineIterator endLine); void erase(LineIterator begin); void erase(LineIterator begin, u64 count); - void erase(u64 start, u64 length = (u64) -1); + void erase(u64 start, i64 length = -1); void clear(); void setLine(const std::string &text); void setLine(const Line &text); bool needsUpdate() const; + bool isEndOfLine(i32 column); + private: + std::string m_chars; + std::string m_colors; + std::string m_flags; + bool m_colorized = false; + i32 m_lineMaxColumn; }; using Lines = std::vector; @@ -382,9 +366,7 @@ namespace hex::ui { struct LanguageDefinition { typedef std::pair TokenRegexString; typedef std::vector TokenRegexStrings; - - typedef bool(*TokenizeCallback)(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, - strConstIter &out_end, PaletteIndex &paletteIndex); + typedef bool(*TokenizeCallback)(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end, PaletteIndex &paletteIndex); std::string m_name; Keywords m_keywords; @@ -397,11 +379,8 @@ namespace hex::ui { TokenRegexStrings m_tokenRegexStrings; bool m_caseSensitive; - LanguageDefinition() : m_name(""), m_keywords({}), m_identifiers({}), m_preprocIdentifiers({}), - m_singleLineComment(""), m_commentEnd(""), - m_commentStart(""), m_globalDocComment(""), m_docComment(""), m_blockDocComment(""), - m_preprocChar('#'), m_autoIndentation(true), m_tokenize(nullptr), - m_tokenRegexStrings({}), m_caseSensitive(true) {} + LanguageDefinition() : m_name(""), m_keywords({}), m_identifiers({}), m_preprocIdentifiers({}), m_singleLineComment(""), m_commentEnd(""), m_commentStart(""), m_globalDocComment(""), + m_docComment(""), m_blockDocComment(""), m_preprocChar('#'), m_autoIndentation(true), m_tokenize(nullptr), m_tokenRegexStrings({}), m_caseSensitive(true) {} static const LanguageDefinition &CPlusPlus(); static const LanguageDefinition &HLSL(); @@ -411,30 +390,29 @@ namespace hex::ui { static const LanguageDefinition &AngelScript(); static const LanguageDefinition &Lua(); }; + TextEditor(); ~TextEditor(); - private: class UndoRecord { public: + friend class TextEditor; UndoRecord() {} - ~UndoRecord() {} - UndoRecord( const std::string &added, - const TextEditor::Selection addedSelection, + const TextEditor::Range addedRange, const std::string &removed, - const TextEditor::Selection removedSelection, + const TextEditor::Range removedRange, TextEditor::EditorState &before, TextEditor::EditorState &after); void undo(TextEditor *editor); void redo(TextEditor *editor); - + private: std::string m_added; - Selection m_addedSelection; + Range m_addedRange; std::string m_removed; - Selection m_removedSelection; + Range m_removedRange; EditorState m_before; EditorState m_after; }; @@ -521,8 +499,8 @@ namespace hex::ui { static const Palette &getRetroBluePalette(); bool isColorizerEnabled() const { return m_colorizerEnabled; } const LanguageDefinition &getLanguageDefinition() const { return m_languageDefinition; } - void setNeedsUpdate(u32 line, bool needsUpdate); - void setColorizedLine(u64 line, const std::string &tokens); + void setNeedsUpdate(i32 line, bool needsUpdate); + void setColorizedLine(i64 line, const std::string &tokens); private: void colorizeRange(); void colorizeInternal(); @@ -558,8 +536,8 @@ namespace hex::ui { inline void setHandleKeyboardInputs(bool value) { m_handleKeyboardInputs = value; } inline bool isHandleKeyboardInputsEnabled() const { return m_handleKeyboardInputs; } private: - std::string getText(const Selection &selection); - void deleteRange(const Selection &selection); + std::string getText(const Range &selection); + void deleteRange(const Range &selection); i32 insertTextAt(Coordinates &where, const std::string &value); void removeLine(i32 start, i32 end); void removeLine(i32 index); @@ -587,7 +565,7 @@ namespace hex::ui { private: Coordinates setCoordinates(const Coordinates &value); Coordinates setCoordinates(i32 line, i32 column); - Selection setCoordinates(const Selection &value); + Range setCoordinates(const Range &value); void advance(Coordinates &coordinates) const; Coordinates findWordStart(const Coordinates &from); Coordinates findWordEnd(const Coordinates &from); @@ -596,8 +574,8 @@ namespace hex::ui { u32 skipSpaces(const Coordinates &from); //Support public: - void setSelection(const Selection &selection); - Selection getSelection() const; + void setSelection(const Range &selection); + Range getSelection() const; void selectWordUnderCursor(); void selectAll(); bool hasSelection() const; @@ -611,7 +589,7 @@ namespace hex::ui { inline bool isImGuiChildIgnored() const { return m_ignoreImGuiChild; } bool raiseContextMenu() { return m_raiseContextMenu; } void clearRaiseContextMenu() { m_raiseContextMenu = false; } - TextEditor *GetSourceCodeEditor(); + TextEditor *getSourceCodeEditor(); bool isEmpty() const; private: void addUndo(UndoRecord &value); @@ -623,17 +601,17 @@ namespace hex::ui { static i32 imTextCharToUtf8(char *buffer, i32 buf_size, u32 c); static void imTextCharToUtf8(std::string &buffer, u32 c); static i32 utf8CharLength(uint8_t c); - static i32 getStringCharacterCount(const std::string &str); + static i32 stringCharacterCount(const std::string &str); static TextEditor::Coordinates stringIndexToCoordinates(i32 strIndex, const std::string &input); + i32 lineMaxColumn(i32 lineIndex); private: Coordinates screenPosToCoordinates(const ImVec2 &position); Coordinates lineCoordsToIndexCoords(const Coordinates &coordinates) const; i32 lineCoordinatesToIndex(const Coordinates &coordinates) const; Coordinates getCharacterCoordinates(i32 line, i32 index); - i32 getLineCharColumn(i32 lineIndex, i32 stringIndex); + i32 lineIndexColumn(i32 lineIndex, i32 stringIndex); u64 getLineByteCount(i32 line) const; - i32 getLineMaxCharColumn(i32 lineIndex); public: FindReplaceHandler m_findReplaceHandler; @@ -679,7 +657,7 @@ namespace hex::ui { ErrorGotoBoxes m_errorGotoBoxes = {}; CursorBoxes m_cursorBoxes = {}; ImVec2 m_charAdvance = {}; - Selection m_interactiveSelection = {}; + Range m_interactiveSelection = {}; u64 m_startTime = 0; std::vector m_defines; TextEditor *m_sourceCodeEditor = nullptr; @@ -696,6 +674,9 @@ namespace hex::ui { std::vector m_clickableText; + constexpr static char inComment = 7; + inline static const Line m_emptyLine = Line(); + inline static const Coordinates Invalid = Coordinates(0x80000000, 0x80000000); static const i32 s_cursorBlinkInterval; static const i32 s_cursorBlinkOnTime; static ImVec2 s_cursorScreenPosition; diff --git a/plugins/ui/source/ui/text_editor/editor.cpp b/plugins/ui/source/ui/text_editor/editor.cpp index bc2232713..93283bf6b 100644 --- a/plugins/ui/source/ui/text_editor/editor.cpp +++ b/plugins/ui/source/ui/text_editor/editor.cpp @@ -27,7 +27,7 @@ namespace hex::ui { TextEditor::~TextEditor() { } - std::string TextEditor::getText(const Selection &from) { + std::string TextEditor::getText(const Range &from) { std::string result; auto selection = setCoordinates(from); auto columns = selection.getSelectedColumns(); @@ -45,10 +45,10 @@ namespace hex::ui { return result; } - void TextEditor::deleteRange(const Selection &rangeToDelete) { + void TextEditor::deleteRange(const Range &rangeToDelete) { if (m_readOnly) return; - Selection selection = setCoordinates(rangeToDelete); + Range selection = setCoordinates(rangeToDelete); auto columns = selection.getSelectedColumns(); if (selection.isSingleLine()) { @@ -100,7 +100,7 @@ namespace hex::ui { auto stringVector = wolv::util::splitString(value, "\n", false); auto lineCount = (i32) stringVector.size(); where.m_line += lineCount - 1; - where.m_column += getStringCharacterCount(stringVector[lineCount - 1]); + where.m_column += stringCharacterCount(stringVector[lineCount - 1]); stringVector[lineCount - 1].append(line.substr(start.m_column,(u64) -1, Line::LinePart::Utf8)); line.erase(start.m_column, (u64) -1); @@ -117,14 +117,14 @@ namespace hex::ui { void TextEditor::deleteWordLeft() { const auto wordEnd = getCursorPosition(); const auto wordStart = findPreviousWord(getCursorPosition()); - setSelection(Selection(wordStart, wordEnd)); + setSelection(Range(wordStart, wordEnd)); backspace(); } void TextEditor::deleteWordRight() { const auto wordStart = getCursorPosition(); const auto wordEnd = findNextWord(getCursorPosition()); - setSelection(Selection(wordStart, wordEnd)); + setSelection(Range(wordStart, wordEnd)); backspace(); } @@ -214,9 +214,9 @@ namespace hex::ui { if (!m_readOnly && undo) { u.m_before = m_state; u.m_removed = getText(); - u.m_removedSelection.m_start = setCoordinates(0, 0); - u.m_removedSelection.m_end = setCoordinates(-1, -1); - if (u.m_removedSelection.m_start == Invalid || u.m_removedSelection.m_end == Invalid) + u.m_removedRange.m_start = setCoordinates(0, 0); + u.m_removedRange.m_end = setCoordinates(-1, -1); + if (u.m_removedRange.m_start == Invalid || u.m_removedRange.m_end == Invalid) return; } auto vectorString = wolv::util::splitString(text, "\n", false); @@ -236,9 +236,9 @@ namespace hex::ui { } if (!m_readOnly && undo) { u.m_added = text; - u.m_addedSelection.m_start = setCoordinates(0, 0); - u.m_addedSelection.m_end = setCoordinates(-1, -1); - if (u.m_addedSelection.m_start == Invalid || u.m_addedSelection.m_end == Invalid) + u.m_addedRange.m_start = setCoordinates(0, 0); + u.m_addedRange.m_end = setCoordinates(-1, -1); + if (u.m_addedRange.m_start == Invalid || u.m_addedRange.m_end == Invalid) return; } m_textChanged = true; @@ -275,10 +275,10 @@ namespace hex::ui { --end.m_line; if (end.m_line >= (i32) m_lines.size()) end.m_line = isEmpty() ? 0 : (i32) m_lines.size() - 1; - end.m_column = getLineMaxCharColumn(end.m_line); + end.m_column = lineMaxColumn(end.m_line); - u.m_removedSelection = Selection(start, end); - u.m_removed = getText(u.m_removedSelection); + u.m_removedRange = Range(start, end); + u.m_removed = getText(u.m_removedRange); bool modified = false; @@ -312,19 +312,19 @@ namespace hex::ui { if (end == Invalid) return; rangeEnd = end; - u.m_added = getText(Selection(start, end)); + u.m_added = getText(Range(start, end)); } else { end = setCoordinates(originalEnd.m_line, 0); rangeEnd = setCoordinates(end.m_line - 1, -1); if (end == Invalid || rangeEnd == Invalid) return; - u.m_added = getText(Selection(start, rangeEnd)); + u.m_added = getText(Range(start, rangeEnd)); } - u.m_addedSelection = Selection(start, rangeEnd); + u.m_addedRange = Range(start, rangeEnd); u.m_after = m_state; - m_state.m_selection = Selection(start, end); + m_state.m_selection = Range(start, end); addUndo(u); m_textChanged = true; @@ -336,13 +336,13 @@ namespace hex::ui { } // c == '\t' else { u.m_removed = getSelectedText(); - u.m_removedSelection = Selection(m_state.m_selection); + u.m_removedRange = Range(m_state.m_selection); deleteSelection(); } } // HasSelection auto coord = setCoordinates(m_state.m_cursorPosition); - u.m_addedSelection.m_start = coord; + u.m_addedRange.m_start = coord; if (m_lines.empty()) m_lines.push_back(Line()); @@ -372,7 +372,7 @@ namespace hex::ui { line.m_colorized = false; setCursorPosition(getCharacterCoordinates(coord.m_line + 1, charPosition)); u.m_added = (char) character; - u.m_addedSelection.m_end = setCoordinates(m_state.m_cursorPosition); + u.m_addedRange.m_end = setCoordinates(m_state.m_cursorPosition); } else if (character == '\t') { auto &line = m_lines[coord.m_line]; auto charIndex = lineCoordinatesToIndex(coord); @@ -384,7 +384,7 @@ namespace hex::ui { line.m_colorized = false; setCursorPosition(getCharacterCoordinates(coord.m_line, charIndex + spacesToInsert)); u.m_added = spaces; - u.m_addedSelection.m_end = setCoordinates(m_state.m_cursorPosition); + u.m_addedRange.m_end = setCoordinates(m_state.m_cursorPosition); } else { auto spacesToRemove = (charIndex % m_tabSize); if (spacesToRemove == 0) spacesToRemove = m_tabSize; @@ -399,11 +399,11 @@ namespace hex::ui { } std::string spaces(spacesRemoved, ' '); u.m_removed = spaces; - u.m_removedSelection = Selection(Coordinates(coord.m_line, charIndex), Coordinates(coord.m_line, charIndex + spacesRemoved)); + u.m_removedRange = Range(Coordinates(coord.m_line, charIndex), Coordinates(coord.m_line, charIndex + spacesRemoved)); line.m_colorized = false; setCursorPosition(getCharacterCoordinates(coord.m_line, std::max(0, charIndex))); } - u.m_addedSelection.m_end = setCoordinates(m_state.m_cursorPosition); + u.m_addedRange.m_end = setCoordinates(m_state.m_cursorPosition); } else { std::string buf = ""; imTextCharToUtf8(buf, character); @@ -414,15 +414,15 @@ namespace hex::ui { if (m_overwrite && charIndex < (i32) line.size()) { i64 column = coord.m_column; std::string c = line[column]; - auto charCount = getStringCharacterCount(c); + auto charCount = stringCharacterCount(c); auto d = c.size(); - u.m_removedSelection = Selection(m_state.m_cursorPosition, getCharacterCoordinates(coord.m_line, coord.m_column + charCount)); + u.m_removedRange = Range(m_state.m_cursorPosition, getCharacterCoordinates(coord.m_line, coord.m_column + charCount)); u.m_removed = std::string(line.m_chars.begin() + charIndex, line.m_chars.begin() + charIndex + d); line.erase(line.begin() + charIndex, d); line.m_colorized = false; } - auto charCount = getStringCharacterCount(buf); + auto charCount = stringCharacterCount(buf); if (buf == "{") buf += "}"; else if (buf == "[") @@ -455,7 +455,7 @@ namespace hex::ui { line.insert(line.begin() + charIndex, buf.begin(), buf.end()); line.m_colorized = false; u.m_added = buf; - u.m_addedSelection.m_end = getCharacterCoordinates(coord.m_line, charIndex + buf.size()); + u.m_addedRange.m_end = getCharacterCoordinates(coord.m_line, charIndex + buf.size()); setCursorPosition(getCharacterCoordinates(coord.m_line, charIndex + charCount)); } else return; @@ -491,7 +491,7 @@ namespace hex::ui { insertTextAt(pos, value); m_lines[pos.m_line].m_colorized = false; - setSelection(Selection(pos, pos)); + setSelection(Range(pos, pos)); setCursorPosition(pos); refreshSearchResults(); colorize(); @@ -504,7 +504,7 @@ namespace hex::ui { deleteRange(m_state.m_selection); - setSelection(Selection(m_state.m_selection.m_start, m_state.m_selection.m_start)); + setSelection(Range(m_state.m_selection.m_start, m_state.m_selection.m_start)); setCursorPosition(m_state.m_selection.m_start); refreshSearchResults(); colorize(); @@ -521,20 +521,20 @@ namespace hex::ui { if (hasSelection()) { u.m_removed = getSelectedText(); - u.m_removedSelection = m_state.m_selection; + u.m_removedRange = m_state.m_selection; deleteSelection(); } else { auto pos = setCoordinates(m_state.m_cursorPosition); setCursorPosition(pos); auto &line = m_lines[pos.m_line]; - if (pos.m_column == getLineMaxCharColumn(pos.m_line)) { + if (pos.m_column == lineMaxColumn(pos.m_line)) { if (pos.m_line == (i32) m_lines.size() - 1) return; u.m_removed = '\n'; - u.m_removedSelection.m_start = u.m_removedSelection.m_end = setCoordinates(m_state.m_cursorPosition); - advance(u.m_removedSelection.m_end); + u.m_removedRange.m_start = u.m_removedRange.m_end = setCoordinates(m_state.m_cursorPosition); + advance(u.m_removedRange.m_end); auto &nextLine = m_lines[pos.m_line + 1]; line.insert(line.end(), nextLine.begin(), nextLine.end()); @@ -543,9 +543,9 @@ namespace hex::ui { } else { i64 charIndex = lineCoordinatesToIndex(pos); - u.m_removedSelection.m_start = u.m_removedSelection.m_end = setCoordinates(m_state.m_cursorPosition); - u.m_removedSelection.m_end.m_column++; - u.m_removed = getText(u.m_removedSelection); + u.m_removedRange.m_start = u.m_removedRange.m_end = setCoordinates(m_state.m_cursorPosition); + u.m_removedRange.m_end.m_column++; + u.m_removed = getText(u.m_removedRange); auto d = utf8CharLength(line[charIndex][0]); line.erase(line.begin() + charIndex, d); @@ -572,7 +572,7 @@ namespace hex::ui { if (hasSelection()) { u.m_removed = getSelectedText(); - u.m_removedSelection = m_state.m_selection; + u.m_removedRange = m_state.m_selection; deleteSelection(); } else { auto pos = setCoordinates(m_state.m_cursorPosition); @@ -583,11 +583,11 @@ namespace hex::ui { return; u.m_removed = '\n'; - u.m_removedSelection.m_start = u.m_removedSelection.m_end = setCoordinates(pos.m_line - 1, -1); - advance(u.m_removedSelection.m_end); + u.m_removedRange.m_start = u.m_removedRange.m_end = setCoordinates(pos.m_line - 1, -1); + advance(u.m_removedRange.m_end); auto &prevLine = m_lines[pos.m_line - 1]; - auto prevSize = getLineMaxCharColumn(pos.m_line - 1); + auto prevSize = lineMaxColumn(pos.m_line - 1); if (prevSize == 0) prevLine = line; else @@ -625,7 +625,7 @@ namespace hex::ui { m_state.m_cursorPosition.m_column += 1; } } - u.m_removedSelection = Selection(pos, m_state.m_cursorPosition); + u.m_removedRange = Range(pos, m_state.m_cursorPosition); u.m_removed = charToRemove; auto charStart = lineCoordinatesToIndex(pos); auto charEnd = lineCoordinatesToIndex(m_state.m_cursorPosition); @@ -666,12 +666,12 @@ namespace hex::ui { auto lineIndex = setCoordinates(m_state.m_cursorPosition).m_line; if (lineIndex < 0 || lineIndex >= (i32) m_lines.size()) return; - setSelection(Selection(setCoordinates(lineIndex, 0), setCoordinates(lineIndex + 1, 0))); + setSelection(Range(setCoordinates(lineIndex, 0), setCoordinates(lineIndex + 1, 0))); } UndoRecord u; u.m_before = m_state; u.m_removed = getSelectedText(); - u.m_removedSelection = m_state.m_selection; + u.m_removedRange = m_state.m_selection; copy(); deleteSelection(); @@ -691,15 +691,15 @@ namespace hex::ui { if (hasSelection()) { u.m_removed = getSelectedText(); - u.m_removedSelection = m_state.m_selection; + u.m_removedRange = m_state.m_selection; deleteSelection(); } u.m_added = clipTextStr; - u.m_addedSelection.m_start = setCoordinates(m_state.m_cursorPosition); + u.m_addedRange.m_start = setCoordinates(m_state.m_cursorPosition); insertText(clipTextStr); - u.m_addedSelection.m_end = setCoordinates(m_state.m_cursorPosition); + u.m_addedRange.m_end = setCoordinates(m_state.m_cursorPosition); u.m_after = m_state; addUndo(u); } @@ -748,7 +748,7 @@ namespace hex::ui { auto end = setCoordinates(-1, -1); if (start == Invalid || end == Invalid) return ""; - return getText(Selection(start, end)); + return getText(Range(start, end)); } std::vector TextEditor::getTextLines() const { @@ -773,25 +773,25 @@ namespace hex::ui { auto endLine = setCoordinates(line, -1); if (sanitizedLine == Invalid || endLine == Invalid) return ""; - return getText(Selection(sanitizedLine, endLine)); + return getText(Range(sanitizedLine, endLine)); } TextEditor::UndoRecord::UndoRecord( const std::string &added, - const TextEditor::Selection addedSelection, + const TextEditor::Range addedSelection, const std::string &removed, - const TextEditor::Selection removedSelection, + const TextEditor::Range removedSelection, TextEditor::EditorState &before, - TextEditor::EditorState &after) : m_added(added), m_addedSelection(addedSelection), m_removed(removed), m_removedSelection(removedSelection), m_before(before), m_after(after) {} + TextEditor::EditorState &after) : m_added(added), m_addedRange(addedSelection), m_removed(removed), m_removedRange(removedSelection), m_before(before), m_after(after) {} void TextEditor::UndoRecord::undo(TextEditor *editor) { if (!m_added.empty()) { - editor->deleteRange(m_addedSelection); + editor->deleteRange(m_addedRange); editor->colorize(); } if (!m_removed.empty()) { - auto start = m_removedSelection.m_start; + auto start = m_removedRange.m_start; editor->insertTextAt(start, m_removed.c_str()); editor->colorize(); } @@ -802,12 +802,12 @@ namespace hex::ui { void TextEditor::UndoRecord::redo(TextEditor *editor) { if (!m_removed.empty()) { - editor->deleteRange(m_removedSelection); + editor->deleteRange(m_removedRange); editor->colorize(); } if (!m_added.empty()) { - auto start = m_addedSelection.m_start; + auto start = m_addedRange.m_start; editor->insertTextAt(start, m_added.c_str()); editor->colorize(); } diff --git a/plugins/ui/source/ui/text_editor/highlighter.cpp b/plugins/ui/source/ui/text_editor/highlighter.cpp index 2bdbd2f6f..f09711a09 100644 --- a/plugins/ui/source/ui/text_editor/highlighter.cpp +++ b/plugins/ui/source/ui/text_editor/highlighter.cpp @@ -15,13 +15,13 @@ namespace hex::ui { return first1 == last1 && first2 == last2; } - void TextEditor::setNeedsUpdate(u32 line, bool needsUpdate) { - if (line < m_lines.size()) + void TextEditor::setNeedsUpdate(i32 line, bool needsUpdate) { + if (line < (i32) m_lines.size()) m_lines[line].setNeedsUpdate(needsUpdate); } - void TextEditor::setColorizedLine(u64 line, const std::string &tokens) { - if (line < m_lines.size()) { + void TextEditor::setColorizedLine(i64 line, const std::string &tokens) { + if (line < (i64)m_lines.size()) { auto &lineTokens = m_lines[line].m_colors; if (lineTokens.size() != tokens.size()) { lineTokens.resize(tokens.size()); @@ -103,7 +103,7 @@ namespace hex::ui { token_color = PaletteIndex::GlobalVariable; } } else { - if ((token_color == PaletteIndex::Identifier || flags.m_bits.preprocessor) && !flags.m_bits.deactivated && !(flags.m_value & Line::inComment)) { + if ((token_color == PaletteIndex::Identifier || flags.m_bits.preprocessor) && !flags.m_bits.deactivated && !(flags.m_value & inComment)) { id.assign(token_begin, token_end); if (m_languageDefinition.m_preprocIdentifiers.count(id) != 0) { token_color = PaletteIndex::Directive; @@ -115,7 +115,7 @@ namespace hex::ui { token_length = token_end - token_begin; } } - if (flags.m_bits.matchedBracket) { + if (flags.m_bits.matchedDelimiter) { token_color = PaletteIndex::WarningText; token_length = token_end - token_begin; } else if (flags.m_bits.preprocessor && !flags.m_bits.deactivated) { @@ -125,7 +125,7 @@ namespace hex::ui { token_color = PaletteIndex::PreprocessorDeactivated; token_begin -= 1; token_offset -= 1; - } else if (id.assign(token_begin, token_end);flags.m_value & Line::inComment && m_languageDefinition.m_preprocIdentifiers.count(id) != 0) { + } else if (id.assign(token_begin, token_end);flags.m_value & inComment && m_languageDefinition.m_preprocIdentifiers.count(id) != 0) { token_color = getColorIndexFromFlags(flags); token_begin -= 1; token_offset -= 1; @@ -202,13 +202,18 @@ namespace hex::ui { auto setGlyphFlags = [&](i32 index) { Line::Flags flags(0); - flags.m_bits.comment = withinComment; - flags.m_bits.blockComment = withinBlockComment; - flags.m_bits.docComment = withinDocComment; - flags.m_bits.globalDocComment = withinGlobalDocComment; - flags.m_bits.blockDocComment = withinBlockDocComment; + if (withinComment) + flags.m_value = (i32) Line::Comments::Line; + else if (withinDocComment) + flags.m_value = (i32) Line::Comments::Doc; + else if (withinBlockComment) + flags.m_value = (i32) Line::Comments::Block; + else if (withinGlobalDocComment) + flags.m_value = (i32) Line::Comments::Global; + else if (withinBlockDocComment) + flags.m_value = (i32) Line::Comments::BlockDoc; flags.m_bits.deactivated = withinNotDef; - flags.m_bits.matchedBracket = matchedBracket; + flags.m_bits.matchedDelimiter = matchedBracket; if (m_lines[currentLine].m_flags[index] != flags.m_value) { m_lines[currentLine].m_colorized = false; m_lines[currentLine].m_flags[index] = flags.m_value; @@ -227,12 +232,31 @@ namespace hex::ui { if (m_matchedBracket.m_nearCursor == getCharacterCoordinates(currentLine, currentIndex) || m_matchedBracket.m_matched == getCharacterCoordinates(currentLine, currentIndex)) matchedBracket = true; } else if (MatchedBracket::s_operators.contains(c) && m_matchedBracket.isActive()) { - if (m_matchedBracket.m_nearCursor == setCoordinates(currentLine, currentIndex) || m_matchedBracket.m_matched == setCoordinates(currentLine, currentIndex)) { - if ((c == '<' && (line.m_colors[currentIndex - 1] == static_cast(PaletteIndex::UserDefinedType))) || - (c == '>' && (m_matchedBracket.m_matched.m_column > 0) && (line.m_colors[lineCoordinatesToIndex(m_matchedBracket.m_matched) - 1] == static_cast(PaletteIndex::UserDefinedType) || - ((m_matchedBracket.m_nearCursor.m_column > 0) && (line.m_colors[lineCoordinatesToIndex(m_matchedBracket.m_nearCursor) - 1] == static_cast(PaletteIndex::UserDefinedType)))))) { - matchedBracket = true; + Coordinates current = setCoordinates(currentLine,currentIndex); + auto udt = static_cast(PaletteIndex::UserDefinedType); + Coordinates cursor = Invalid; + //if (m_matchedBracket.m_nearCursor == setCoordinates(currentLine, currentIndex) || m_matchedBracket.m_matched == setCoordinates(currentLine, currentIndex)) { + if ((c == '<' && m_matchedBracket.m_nearCursor == current) || (c == '>' && m_matchedBracket.m_matched == current)) + cursor = m_matchedBracket.m_nearCursor; + else if ((c == '>' && m_matchedBracket.m_nearCursor == current) || (c == '<' && m_matchedBracket.m_matched == current)) + cursor = m_matchedBracket.m_matched; + + if (cursor != Invalid) { + if (cursor.m_column == 0 && cursor.m_line > 0) { + cursor.m_line--; + cursor.m_column = m_lines[cursor.m_line].m_colors.size() - 1; + } else if (cursor.m_column > 0) { + cursor.m_column--; } + while (std::isblank(m_lines[cursor.m_line].m_colors[cursor.m_column]) && (cursor.m_line != 0 || cursor.m_column != 0)) { + if (cursor.m_column == 0 && cursor.m_line > 0) { + cursor.m_line--; + cursor.m_column = m_lines[cursor.m_line].m_colors.size() - 1; + } else + cursor.m_column--; + } + if (m_lines[cursor.m_line].m_colors[cursor.m_column] == udt && (cursor.m_line != 0 || cursor.m_column != 0)) + matchedBracket = true; } } @@ -240,7 +264,7 @@ namespace hex::ui { if (c != m_languageDefinition.m_preprocChar && !isspace(c)) firstChar = false; - bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= (i64) currentIndex)); + bool isComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= (i64) currentIndex)); if (withinString) { setGlyphFlags(currentIndex); @@ -250,7 +274,7 @@ namespace hex::ui { } else if (c == '\"') withinString = false; } else { - if (firstChar && c == m_languageDefinition.m_preprocChar && !inComment && !withinComment && !withinDocComment && !withinString) { + if (firstChar && c == m_languageDefinition.m_preprocChar && !isComment && !withinComment && !withinDocComment && !withinString) { withinPreproc = true; std::string directive; auto start = currentIndex + 1; @@ -271,21 +295,23 @@ namespace hex::ui { identifier += line[start]; start++; } + auto definesBegin = m_defines.begin(); + auto definesEnd = m_defines.end(); if (directive == "define") { - if (identifier.size() > 0 && !withinNotDef && std::find(m_defines.begin(), m_defines.end(), identifier) == m_defines.end()) + if (identifier.size() > 0 && !withinNotDef && std::find(definesBegin, definesEnd, identifier) == definesEnd) m_defines.push_back(identifier); } else if (directive == "undef") { if (identifier.size() > 0 && !withinNotDef) - m_defines.erase(std::remove(m_defines.begin(), m_defines.end(), identifier), m_defines.end()); + m_defines.erase(std::remove(definesBegin, definesEnd, identifier), definesEnd); } else if (directive == "ifdef") { if (!withinNotDef) { - bool isConditionMet = std::find(m_defines.begin(), m_defines.end(), identifier) != m_defines.end(); + bool isConditionMet = std::find(definesBegin, definesEnd, identifier) != definesEnd; ifDefs.push_back(isConditionMet); } else ifDefs.push_back(false); } else if (directive == "ifndef") { if (!withinNotDef) { - bool isConditionMet = std::find(m_defines.begin(), m_defines.end(), identifier) == m_defines.end(); + bool isConditionMet = std::find(definesBegin, definesEnd, identifier) == definesEnd; ifDefs.push_back(isConditionMet); } else ifDefs.push_back(false); @@ -293,7 +319,7 @@ namespace hex::ui { } } - if (c == '\"' && !withinPreproc && !inComment && !withinComment && !withinDocComment) { + if (c == '\"' && !withinPreproc && !isComment && !withinComment && !withinDocComment) { withinString = true; setGlyphFlags(currentIndex); } else { @@ -307,12 +333,12 @@ namespace hex::ui { return !a.empty() && currentIndex + 1 >= 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 (!isComment && !withinComment && !withinDocComment && !withinPreproc && !withinString) { if (compareForth(m_languageDefinition.m_docComment, line.m_chars)) { - withinDocComment = !inComment; + withinDocComment = !isComment; commentLength = 3; } else if (compareForth(m_languageDefinition.m_singleLineComment, line.m_chars)) { - withinComment = !inComment; + withinComment = !isComment; commentLength = 2; } else { bool isGlobalDocComment = compareForth(m_languageDefinition.m_globalDocComment, line.m_chars); @@ -338,7 +364,7 @@ namespace hex::ui { } } } - inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= (i64) currentIndex)); + isComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= (i64) currentIndex)); } setGlyphFlags(currentIndex); diff --git a/plugins/ui/source/ui/text_editor/navigate.cpp b/plugins/ui/source/ui/text_editor/navigate.cpp index c0487b684..3b5c103c0 100644 --- a/plugins/ui/source/ui/text_editor/navigate.cpp +++ b/plugins/ui/source/ui/text_editor/navigate.cpp @@ -18,7 +18,7 @@ namespace hex::ui { } void TextEditor::jumpToCoords(const Coordinates &coords) { - setSelection(Selection(coords, coords)); + setSelection(Range(coords, coords)); setCursorPosition(coords); ensureCursorVisible(); @@ -38,7 +38,7 @@ namespace hex::ui { else if (oldPos == m_interactiveSelection.m_end) m_interactiveSelection.m_end = newPos; else { - m_interactiveSelection = Selection(newPos, oldPos); + m_interactiveSelection = Range(newPos, oldPos); } } else m_interactiveSelection.m_start = m_interactiveSelection.m_end = newPos; @@ -124,7 +124,7 @@ namespace hex::ui { return; auto lindex = m_state.m_cursorPosition.m_line; - auto lineMaxColumn = getLineMaxCharColumn(lindex); + auto lineMaxColumn = this->lineMaxColumn(lindex); auto column = std::min(m_state.m_cursorPosition.m_column, lineMaxColumn); while (amount-- > 0) { @@ -167,7 +167,7 @@ namespace hex::ui { return; auto lindex = m_state.m_cursorPosition.m_line; - auto lineMaxColumn = getLineMaxCharColumn(lindex); + auto lineMaxColumn = this->lineMaxColumn(lindex); auto column = std::min(m_state.m_cursorPosition.m_column, lineMaxColumn); while (amount-- > 0) { @@ -207,7 +207,7 @@ namespace hex::ui { if (m_state.m_cursorPosition != oldPos) { if (select) { - m_interactiveSelection = Selection(m_state.m_cursorPosition, oldPos); + m_interactiveSelection = Range(m_state.m_cursorPosition, oldPos); } else m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; setSelection(m_interactiveSelection); @@ -220,7 +220,7 @@ namespace hex::ui { auto newPos = setCoordinates(-1, -1); setCursorPosition(newPos); if (select) { - m_interactiveSelection = Selection(oldPos, newPos); + m_interactiveSelection = Range(oldPos, newPos); } else m_interactiveSelection.m_start = m_interactiveSelection.m_end = newPos; setSelection(m_interactiveSelection); @@ -245,7 +245,7 @@ namespace hex::ui { else { postIdx = postfix.find_first_not_of(" "); if (postIdx == std::string::npos) - home = getLineMaxCharColumn(oldPos.m_line); + home = this->lineMaxColumn(oldPos.m_line); else if (postIdx == 0) home = 0; else @@ -260,7 +260,7 @@ namespace hex::ui { else { postIdx = postfix.find_first_not_of(" "); if (postIdx == std::string::npos) - home = getLineMaxCharColumn(oldPos.m_line); + home = lineMaxColumn(oldPos.m_line); else home = oldPos.m_column + postIdx; } @@ -286,7 +286,7 @@ namespace hex::ui { void TextEditor::moveEnd(bool select) { resetCursorBlinkTime(); auto oldPos = m_state.m_cursorPosition; - setCursorPosition(setCoordinates(m_state.m_cursorPosition.m_line, getLineMaxCharColumn(oldPos.m_line))); + setCursorPosition(setCoordinates(m_state.m_cursorPosition.m_line, lineMaxColumn(oldPos.m_line))); if (m_state.m_cursorPosition != oldPos) { if (select) { @@ -326,24 +326,46 @@ namespace hex::ui { setCursorPosition(m_state.m_selection.m_end); } + TextEditor::Coordinates::Coordinates(TextEditor *editor, i32 line, i32 column) + : m_line(line), m_column(column) { + sanitize(editor); + } + + bool TextEditor::Coordinates::isValid(TextEditor *editor) const { + + auto maxLine = editor->m_lines.size(); + if (std::abs(m_line) > (i32) maxLine) + return false; + auto maxColumn = editor->lineMaxColumn(m_line); + if (std::abs(m_column) > maxColumn) + return false; + return true; + } + + TextEditor::Coordinates TextEditor::Coordinates::sanitize(TextEditor *editor) { + + i32 lineCount = editor->m_lines.size(); + if (m_line < 0) { + m_line = std::clamp(m_line, -lineCount, -1); + m_line = lineCount + m_line; + } else + m_line = std::clamp(m_line, 0, lineCount - 1); + + + auto maxColumn = editor->lineMaxColumn(m_line) + 1; + if (m_column < 0) { + m_column = std::clamp(m_column, -maxColumn, -1); + m_column = maxColumn + m_column; + } else + m_column = std::clamp(m_column, 0, maxColumn); + + return *this; + } + TextEditor::Coordinates TextEditor::setCoordinates(i32 line, i32 column) { if (isEmpty()) return Coordinates(0, 0); - - Coordinates result = Coordinates(0, 0); - auto lineCount = (i32) m_lines.size(); - if (line < 0 && lineCount + line >= 0) - result.m_line = lineCount + line; - else - result.m_line = std::clamp(line, 0, lineCount - 1); - - auto maxColumn = getLineMaxCharColumn(result.m_line) + 1; - if (column < 0 && maxColumn + column >= 0) - result.m_column = maxColumn + column; - else - result.m_column = std::clamp(column, 0, maxColumn - 1); - - return result; + return Coordinates(this, line, column); } TextEditor::Coordinates TextEditor::setCoordinates(const Coordinates &value) { @@ -351,15 +373,15 @@ namespace hex::ui { return sanitized_value; } - TextEditor::Selection TextEditor::setCoordinates(const Selection &value) { + TextEditor::Range TextEditor::setCoordinates(const Range &value) { auto start = setCoordinates(value.m_start); auto end = setCoordinates(value.m_end); if (start == Invalid || end == Invalid) - return Selection(Invalid, Invalid); + return Range(Invalid, Invalid); if (start > end) { std::swap(start, end); } - return Selection(start, end); + return Range(start, end); } void TextEditor::advance(Coordinates &coordinates) const { @@ -373,7 +395,7 @@ namespace hex::ui { auto &line = m_lines[coordinates.m_line]; i64 column = coordinates.m_column; std::string lineChar = line[column]; - auto incr = getStringCharacterCount(lineChar); + auto incr = stringCharacterCount(lineChar); coordinates.m_column += incr; } diff --git a/plugins/ui/source/ui/text_editor/render.cpp b/plugins/ui/source/ui/text_editor/render.cpp index 5f49199f3..0ed48e25c 100644 --- a/plugins/ui/source/ui/text_editor/render.cpp +++ b/plugins/ui/source/ui/text_editor/render.cpp @@ -70,8 +70,7 @@ namespace hex::ui { } float TextEditor::getPageSize() const { - auto height = ImGui::GetCurrentWindow()->InnerClipRect.GetHeight(); - return height / m_charAdvance.y; + return ImGui::GetCurrentWindow()->InnerClipRect.GetHeight() / m_charAdvance.y; } bool TextEditor::isEndOfLine() const { @@ -84,7 +83,7 @@ namespace hex::ui { bool TextEditor::isEndOfLine(const Coordinates &coordinates) const { if (coordinates.m_line < (i32) m_lines.size()) - return coordinates.m_column >= getStringCharacterCount(m_lines[coordinates.m_line].m_chars); + return coordinates.m_column >= stringCharacterCount(m_lines[coordinates.m_line].m_chars); return true; } @@ -196,49 +195,57 @@ namespace hex::ui { auto windowPadding = ImGui::GetStyle().FramePadding * 2.0f; - auto height = ImGui::GetWindowHeight() - m_topMargin - scrollBarSize - m_charAdvance.y; + auto height = ImGui::GetWindowHeight() - m_topMargin - scrollBarSize; auto width = ImGui::GetWindowWidth() - windowPadding.x - scrollBarSize; - auto topPixels = m_topMargin > scrollY ? m_topMargin - scrollY : scrollY; - auto top = (i32) rint(topPixels / m_charAdvance.y) + 1; - top -= (top >= (i32) m_lines.size()); - auto bottom = (i32) rint((topPixels + height) / m_charAdvance.y); + auto top = m_topMargin > scrollY ? m_topMargin - scrollY : scrollY; + auto topRow = (i32) rint(top / m_charAdvance.y); + auto bottomRow = (i32) rint((top + height) / m_charAdvance.y); - auto left = (i32) rint(scrollX / m_charAdvance.x); - auto right = (i32) rint((scrollX + width) / m_charAdvance.x); + auto leftColumnIndex = (i32) rint(scrollX / m_charAdvance.x); + auto rightColumnIndex = (i32) rint((scrollX + width) / m_charAdvance.x); auto pos = setCoordinates(m_state.m_cursorPosition); pos.m_column = (i32) rint(textDistanceToLineStart(pos) / m_charAdvance.x); + auto posColumnIndex = (i32) rint(textDistanceToLineStart(pos) / m_charAdvance.x); + auto posRow = pos.m_line; + bool scrollToCursorX = true; + bool scrollToCursorY = true; - bool mScrollToCursorX = true; - bool mScrollToCursorY = true; + if ((posRow > topRow && posRow < bottomRow) || + (posRow == topRow && topRow == top && scrollY == ImGui::GetScrollMaxY())) + scrollToCursorY = false; - if (pos.m_line >= top && pos.m_line <= bottom) - mScrollToCursorY = false; - if ((pos.m_column >= left) && (pos.m_column <= right)) - mScrollToCursorX = false; - if (!mScrollToCursorX && !mScrollToCursorY && m_oldTopMargin == m_topMargin) { + if ((posColumnIndex >= leftColumnIndex) && (posColumnIndex <= rightColumnIndex)) + scrollToCursorX = false; + + if ((!scrollToCursorX && !scrollToCursorY && m_oldTopMargin == m_topMargin) || pos.m_line < 0) { m_scrollToCursor = false; return; } - if (mScrollToCursorY) { - if (pos.m_line < top) { - ImGui::SetScrollY(std::max(0.0f, (pos.m_line - 1) * m_charAdvance.y)); + if (scrollToCursorY) { + if (posRow <= topRow) { + if (posRow <= 0) { + ImGui::SetScrollY(0.0f); + m_scrollToCursor = false; + return; + } + ImGui::SetScrollY((posRow - 1) * m_charAdvance.y); m_scrollToCursor = true; } - if (pos.m_line > bottom) { - ImGui::SetScrollY(std::max(0.0f, pos.m_line * m_charAdvance.y - height)); + if (posRow >= bottomRow) { + ImGui::SetScrollY((posRow + 1) * m_charAdvance.y - height); m_scrollToCursor = true; } } - if (mScrollToCursorX) { - if (pos.m_column < left) { - ImGui::SetScrollX(std::max(0.0f, pos.m_column * m_charAdvance.x)); + if (scrollToCursorX) { + if (posColumnIndex < leftColumnIndex) { + ImGui::SetScrollX(std::max(0.0f, posColumnIndex * m_charAdvance.x)); m_scrollToCursor = true; } - if (pos.m_column > right) { - ImGui::SetScrollX(std::max(0.0f, pos.m_column * m_charAdvance.x - width)); + if (posColumnIndex > rightColumnIndex) { + ImGui::SetScrollX(std::max(0.0f, posColumnIndex * m_charAdvance.x - width)); m_scrollToCursor = true; } } @@ -285,7 +292,8 @@ namespace hex::ui { if (m_state.m_cursorPosition.m_line == lineNo && m_showCursor && focused) renderCursor(lineNo, drawList); - renderGotoButtons(lineNo); + if (!m_showLineNumbers) + renderGotoButtons(lineNo); // Render colorized text @@ -298,13 +306,13 @@ namespace hex::ui { continue; } auto colors = m_lines[lineNo].m_colors; - auto lineSize = line.getLineTextSize(); + auto lineSize = line.lineTextSize(); i64 colorsSize = std::min((u64)textEditorSize.x, (u64) lineSize); i64 start = ImGui::GetScrollX(); i64 textSize = 0; Coordinates head = Coordinates(lineNo, start / m_charAdvance.x); textSize = textDistanceToLineStart(head); - auto maxColumn = line.getCharColumn(line.size()); + auto maxColumn = line.indexColumn(line.size()); if (textSize < start) { while (textSize < start && head.m_column < maxColumn) { head.m_column += 1; @@ -330,8 +338,8 @@ namespace hex::ui { } } - u64 i = line.getColumnIndex(head.m_column); - u64 maxI = line.getColumnIndex(current.m_column); + u64 i = line.columnIndex(head.m_column); + u64 maxI = line.columnIndex(current.m_column); while (i < maxI) { char color = std::clamp(colors[i], (char) PaletteIndex::Default, (char) ((u8) PaletteIndex::Max - 1)); auto index = colors.find_first_not_of(color, i); @@ -343,7 +351,7 @@ namespace hex::ui { u32 tokenLength = std::clamp((u64) index,(u64) 1, maxI - i); if (m_updateFocus) setFocus(); - auto lineStart = setCoordinates(lineNo, line.getCharColumn(i)); + auto lineStart = setCoordinates(lineNo, line.indexColumn(i)); drawText(lineStart, i, tokenLength, color); @@ -398,7 +406,7 @@ namespace hex::ui { void TextEditor::drawSelection(float lineNo) { ImVec2 lineStartScreenPos = s_cursorScreenPosition + ImVec2(m_leftMargin, m_topMargin + std::floor(lineNo) * m_charAdvance.y); - Selection lineCoords = Selection(setCoordinates(lineNo, 0), setCoordinates(lineNo, -1)); + Range lineCoords = Range(setCoordinates(lineNo, 0), setCoordinates(lineNo, -1)); auto drawList = ImGui::GetWindowDrawList(); if (m_state.m_selection.m_start <= lineCoords.m_end && m_state.m_selection.m_end > lineCoords.m_start) { @@ -485,6 +493,8 @@ namespace hex::ui { } void TextEditor::renderGotoButtons(float lineNo) { + if (isEmpty()) + return; ImVec2 lineStartScreenPos = s_cursorScreenPosition + ImVec2(m_leftMargin, m_topMargin + std::floor(lineNo) * m_charAdvance.y); auto lineText = getLineText(lineNo); Coordinates gotoKey = setCoordinates(lineNo + 1, 0); @@ -510,13 +520,13 @@ namespace hex::ui { if (!errorColumn.empty()) currColumn = std::stoi(errorColumn) - 1; } - TextEditor::Coordinates errorPos = GetSourceCodeEditor()->setCoordinates(currLine, currColumn); + TextEditor::Coordinates errorPos = getSourceCodeEditor()->setCoordinates(currLine, currColumn); if (errorPos != Invalid) { ImVec2 errorStart = ImVec2(lineStartScreenPos.x, lineStartScreenPos.y); auto lineEnd = setCoordinates(lineNo, -1); if (lineEnd != Invalid) { ImVec2 errorEnd = ImVec2(lineStartScreenPos.x + textDistanceToLineStart(lineEnd), lineStartScreenPos.y + m_charAdvance.y); - ErrorGotoBox box = ErrorGotoBox(ImRect({errorStart, errorEnd}), errorPos, GetSourceCodeEditor()); + ErrorGotoBox box = ErrorGotoBox(ImRect({errorStart, errorEnd}), errorPos, getSourceCodeEditor()); m_errorGotoBoxes[gotoKey] = box; CursorChangeBox cursorBox = CursorChangeBox(ImRect({errorStart, errorEnd})); m_cursorBoxes[gotoKey] = cursorBox; @@ -621,16 +631,10 @@ namespace hex::ui { } float TextEditor::textDistanceToLineStart(const Coordinates &aFrom) { - auto &line = m_lines[aFrom.m_line]; - i32 colIndex = lineCoordinatesToIndex(aFrom); - auto substr1 = line.m_chars.substr(0, colIndex); - auto substr2 =line.m_chars.substr(colIndex, line.m_chars.size() - colIndex); - if (substr2.size() < substr1.size()) { - auto distanceToEnd = line.getStringTextSize(substr2.c_str()); - line.m_lineTextSize = line.getLineTextSize(); - return line.m_lineTextSize - distanceToEnd; - } + auto line = m_lines[aFrom.m_line]; + if (line.empty() || aFrom.m_column == 0) + return 0.0f; - return line.getStringTextSize(substr1.c_str()); + return line.textSize(line.columnIndex(aFrom.m_column)); } } \ No newline at end of file diff --git a/plugins/ui/source/ui/text_editor/support.cpp b/plugins/ui/source/ui/text_editor/support.cpp index 958fe4154..a7ec416da 100644 --- a/plugins/ui/source/ui/text_editor/support.cpp +++ b/plugins/ui/source/ui/text_editor/support.cpp @@ -3,70 +3,88 @@ #include namespace hex::ui { - bool TextEditor::Coordinates::operator==(const Coordinates &o) const { - return m_line == o.m_line && m_column == o.m_column; + using Coordinates = TextEditor::Coordinates; + using Line = TextEditor::Line; + using LineIterator = TextEditor::LineIterator; + using Range = TextEditor::Range; + using FindReplaceHandler = TextEditor::FindReplaceHandler; + bool Coordinates::operator==(const Coordinates &o) const { + return m_line == o.m_line && m_column == o.m_column; } - bool TextEditor::Coordinates::operator!=(const Coordinates &o) const { + bool Coordinates::operator!=(const Coordinates &o) const { return m_line != o.m_line || m_column != o.m_column; } - bool TextEditor::Coordinates::operator<(const Coordinates &o) const { + bool Coordinates::operator<(const Coordinates &o) const { if (m_line != o.m_line) return m_line < o.m_line; return m_column < o.m_column; } - bool TextEditor::Coordinates::operator>(const Coordinates &o) const { + bool Coordinates::operator>(const Coordinates &o) const { if (m_line != o.m_line) return m_line > o.m_line; return m_column > o.m_column; } - bool TextEditor::Coordinates::operator<=(const Coordinates &o) const { - if (m_line != o.m_line) - return m_line < o.m_line; - return m_column <= o.m_column; + bool Coordinates::operator<=(const Coordinates &o) const { + return !(*this > o); } - bool TextEditor::Coordinates::operator>=(const Coordinates &o) const { - if (m_line != o.m_line) - return m_line > o.m_line; - return m_column >= o.m_column; + bool Coordinates::operator>=(const Coordinates &o) const { + return !(*this < o); } - TextEditor::Coordinates TextEditor::Coordinates::operator+(const Coordinates &o) const { + Coordinates Coordinates::operator+(const Coordinates &o) const { return Coordinates(m_line + o.m_line, m_column + o.m_column); } - TextEditor::Coordinates TextEditor::Coordinates::operator-(const Coordinates &o) const { + Coordinates Coordinates::operator-(const Coordinates &o) const { return Coordinates(m_line - o.m_line, m_column - o.m_column); } - TextEditor::Coordinates TextEditor::Selection::getSelectedLines() { + bool Range::operator==(const Range &o) const { + return m_start == o.m_start && m_end == o.m_end; + } + bool Range::operator!=(const Range &o) const { + return m_start != o.m_start || m_end != o.m_end; + } + + Coordinates Range::getSelectedLines() { return Coordinates(m_start.m_line, m_end.m_line); } - TextEditor::Coordinates TextEditor::Selection::getSelectedColumns() { + Coordinates Range::getSelectedColumns() { if (isSingleLine()) return Coordinates(m_start.m_column, m_end.m_column - m_start.m_column); return Coordinates(m_start.m_column, m_end.m_column); } - bool TextEditor::Selection::isSingleLine() { + bool Range::isSingleLine() { return m_start.m_line == m_end.m_line; } - bool TextEditor::Selection::contains(Coordinates coordinates, int8_t endsInclusive) { + bool Range::contains(const Range &range, EndsInclusive endsInclusive) const { + return contains(range.m_start, endsInclusive) && contains(range.m_end, endsInclusive); + } + + bool Range::overlaps(const Range &o, EndsInclusive endsInclusive) const { + return contains(o.m_start, endsInclusive) || contains(o.m_end, endsInclusive) || + o.contains(m_start, endsInclusive) || o.contains(m_end, endsInclusive); + } + + // 0 = exclude both ends, 1 = include end, exclude start, 2 = include start, exclude end, 3 = include both ends + bool Range::contains(const Coordinates &coordinates, EndsInclusive endsInclusive) const { bool result = true; - if (endsInclusive & 2) + if ((u8)endsInclusive & 2) result &= m_start <= coordinates; else result &= m_start < coordinates; if (!result) return false; - if (endsInclusive & 1) + if ((u8)endsInclusive & 1) result &= coordinates <= m_end; else result &= coordinates < m_end; @@ -74,11 +92,55 @@ namespace hex::ui { return result; } - char TextEditor::Line::LineIterator::operator*() { + bool Range::containsLine(i32 value, EndsInclusive endsInclusive) const { + bool result = true; + if ((u8)endsInclusive & 2) + result &= m_start.m_line <= value; + else + result &= m_start.m_line < value; + + if (!result) + return false; + if ((u8)endsInclusive & 1) + result &= value <= m_end.m_line; + else + result &= value < m_end.m_line; + + return result; + } + + bool Range::containsColumn(i32 value, EndsInclusive endsInclusive) const { + bool result = true; + if ((u8)endsInclusive & 2) + result &= m_start.m_column <= value; + else + result &= m_start.m_column < value; + + if (!result) + return false; + if ((u8)endsInclusive & 1) + result &= value <= m_end.m_column; + else + result &= value < m_end.m_column; + + return result; + } + + bool Line::operator==(const Line &line) const { + return m_chars == line.m_chars && m_colors == line.m_colors && m_flags == line.m_flags && + m_colorized == line.m_colorized && m_lineMaxColumn == line.m_lineMaxColumn; + } + + bool Line::operator!=(const Line &line) const { + return m_chars != line.m_chars || m_colors != line.m_colors || m_flags != line.m_flags || + m_colorized != line.m_colorized || m_lineMaxColumn != line.m_lineMaxColumn; + } + + char LineIterator::operator*() { return *m_charsIter; } - TextEditor::Line::LineIterator TextEditor::Line::LineIterator::operator++() { + LineIterator LineIterator::operator++() { LineIterator iter = *this; ++iter.m_charsIter; ++iter.m_colorsIter; @@ -86,24 +148,24 @@ namespace hex::ui { return iter; } - TextEditor::Line::LineIterator TextEditor::Line::LineIterator::operator=(const LineIterator &other) { + LineIterator LineIterator::operator=(const LineIterator &other) { m_charsIter = other.m_charsIter; m_colorsIter = other.m_colorsIter; m_flagsIter = other.m_flagsIter; return *this; } - bool TextEditor::Line::LineIterator::operator!=(const LineIterator &other) const { + bool LineIterator::operator!=(const LineIterator &other) const { return m_charsIter != other.m_charsIter || m_colorsIter != other.m_colorsIter || m_flagsIter != other.m_flagsIter; } - bool TextEditor::Line::LineIterator::operator==(const LineIterator &other) const { + bool LineIterator::operator==(const LineIterator &other) const { return m_charsIter == other.m_charsIter && m_colorsIter == other.m_colorsIter && m_flagsIter == other.m_flagsIter; } - TextEditor::Line::LineIterator TextEditor::Line::LineIterator::operator+(i32 n) { + LineIterator LineIterator::operator+(i32 n) { LineIterator iter = *this; iter.m_charsIter += n; iter.m_colorsIter += n; @@ -111,11 +173,11 @@ namespace hex::ui { return iter; } - i32 TextEditor::Line::LineIterator::operator-(LineIterator l) { + i32 LineIterator::operator-(LineIterator l) { return m_charsIter - l.m_charsIter; } - TextEditor::Line::LineIterator TextEditor::Line::begin() const { + LineIterator Line::begin() const { LineIterator iter; iter.m_charsIter = m_chars.begin(); iter.m_colorsIter = m_colors.begin(); @@ -123,7 +185,7 @@ namespace hex::ui { return iter; } - TextEditor::Line::LineIterator TextEditor::Line::end() const { + LineIterator Line::end() const { LineIterator iter; iter.m_charsIter = m_chars.end(); iter.m_colorsIter = m_colors.end(); @@ -131,7 +193,7 @@ namespace hex::ui { return iter; } - TextEditor::Line::LineIterator TextEditor::Line::begin() { + LineIterator Line::begin() { LineIterator iter; iter.m_charsIter = m_chars.begin(); iter.m_colorsIter = m_colors.begin(); @@ -139,7 +201,7 @@ namespace hex::ui { return iter; } - TextEditor::Line::LineIterator TextEditor::Line::end() { + LineIterator Line::end() { LineIterator iter; iter.m_charsIter = m_chars.end(); iter.m_colorsIter = m_colors.end(); @@ -147,29 +209,29 @@ namespace hex::ui { return iter; } - TextEditor::Line &TextEditor::Line::operator=(const Line &line) { + Line &Line::operator=(const Line &line) { m_chars = line.m_chars; m_colors = line.m_colors; m_flags = line.m_flags; m_colorized = line.m_colorized; - m_lineTextSize = line.m_lineTextSize; + m_lineMaxColumn = line.m_lineMaxColumn; return *this; } - TextEditor::Line &TextEditor::Line::operator=(Line &&line) noexcept { + Line &Line::operator=(Line &&line) noexcept { m_chars = std::move(line.m_chars); m_colors = std::move(line.m_colors); m_flags = std::move(line.m_flags); m_colorized = line.m_colorized; - m_lineTextSize = line.m_lineTextSize; + m_lineMaxColumn = line.m_lineMaxColumn; return *this; } - u64 TextEditor::Line::size() const { + u64 Line::size() const { return m_chars.size(); } - char TextEditor::Line::front(LinePart part) const { + char Line::front(LinePart part) const { if (part == LinePart::Chars && !m_chars.empty()) return m_chars.front(); if (part == LinePart::Colors && !m_colors.empty()) @@ -179,7 +241,7 @@ namespace hex::ui { return 0x00; } - std::string TextEditor::Line::frontUtf8(LinePart part) const { + std::string Line::frontUtf8(LinePart part) const { if (part == LinePart::Chars && !m_chars.empty()) return m_chars.substr(0, TextEditor::utf8CharLength(m_chars[0])); if (part == LinePart::Colors && !m_colors.empty()) @@ -189,33 +251,42 @@ namespace hex::ui { return ""; } - void TextEditor::Line::push_back(char c) { + void Line::push_back(char c) { m_chars.push_back(c); m_colors.push_back(0x00); m_flags.push_back(0x00); m_colorized = false; - m_lineTextSize = -1; + m_lineMaxColumn = -1; } - bool TextEditor::Line::empty() const { + bool Line::empty() const { return m_chars.empty(); } - std::string TextEditor::Line::substr(u64 start, u64 length, LinePart part) const { - if (start >= m_chars.size() || m_colors.size() != m_chars.size() || m_flags.size() != m_chars.size()) - return ""; - if (length == (u64) -1 || start + length >= m_chars.size()) - length = m_chars.size() - start; - if (length == 0) - return ""; + std::string Line::substr(u64 start, u64 length, LinePart part) const { + + if (part != LinePart::Utf8) { + if (start >= m_chars.size() || m_colors.size() != m_chars.size() || m_flags.size() != m_chars.size()) + return ""; + if (length == (u64) -1 || start + length >= m_chars.size()) + length = m_chars.size() - start; + if (length == 0) + return ""; + + if (part == LinePart::Chars) + return m_chars.substr(start, length); + if (part == LinePart::Colors) + return m_colors.substr(start, length); + if (part == LinePart::Flags) + return m_flags.substr(start, length); + } else { + if (start >= (u64) maxColumn()) + return ""; + if (length == (u64) -1 || start + length >= (u64) maxColumn()) + length = maxColumn() - start; + if (length == 0) + return ""; - if (part == LinePart::Chars) - return m_chars.substr(start, length); - if (part == LinePart::Colors) - return m_colors.substr(start, length); - if (part == LinePart::Flags) - return m_flags.substr(start, length); - if (part == LinePart::Utf8) { u64 utf8Start = 0; for (u64 utf8Index = 0; utf8Index < start; ++utf8Index) { utf8Start += TextEditor::utf8CharLength(m_chars[utf8Start]); @@ -229,52 +300,76 @@ namespace hex::ui { return ""; } - char TextEditor::Line::operator[](u64 index) const { - index = std::clamp(index, (u64) 0, (u64) (m_chars.size() - 1)); - return m_chars[index]; + Line Line::subLine(u64 start, u64 length) { + if (start >= m_chars.size()) + return const_cast(m_emptyLine); + if (m_colors.size() != m_chars.size()) + m_colors.resize(m_chars.size(), 0x00); + if (m_flags.size() != m_chars.size()) + m_flags.resize(m_chars.size(), 0x00); + if (length == (u64) -1 || start + length >= m_chars.size()) + length = m_chars.size() - start; + if (length == 0) + return const_cast(m_emptyLine); + + std::string chars = m_chars.substr(start, length); + std::string colors = m_colors.substr(start, length); + std::string flags = m_flags.substr(start, length); + Line result(chars, colors, flags); + result.m_colorized = m_colorized; + result.m_lineMaxColumn = result.maxColumn(); + return result; + } + + char Line::operator[](u64 index) const { + i64 signedIndex = std::clamp((i64) index,0 - (i64) m_chars.size(), (i64) (m_chars.size() - 1)); + if (signedIndex < 0) + return m_chars[m_chars.size() + signedIndex]; + return m_chars[signedIndex]; } // C++ can't overload functions based on return type, so use any type other // than u64 to avoid ambiguity. - std::string TextEditor::Line::operator[](i64 column) const { - u64 utf8Length = TextEditor::getStringCharacterCount(m_chars); - u64 index = static_cast(column); - index = std::clamp(index, (u64) 0, utf8Length - 1); - u64 utf8Start = 0; - for (u64 utf8Index = 0; utf8Index < index; ++utf8Index) { + std::string Line::operator[](i64 index) const { + i64 utf8Length = TextEditor::stringCharacterCount(m_chars); + index = std::clamp(index, (i64) -utf8Length, (i64) utf8Length - 1); + if (index < 0) + index = utf8Length + index; + i64 utf8Start = 0; + for (i64 utf8Index = 0; utf8Index < index; ++utf8Index) { utf8Start += TextEditor::utf8CharLength(m_chars[utf8Start]); } - u64 utf8CharLen = TextEditor::utf8CharLength(m_chars[utf8Start]); - if (utf8Start + utf8CharLen > m_chars.size()) + i64 utf8CharLen = TextEditor::utf8CharLength(m_chars[utf8Start]); + if (utf8Start + utf8CharLen > (i64) m_chars.size()) utf8CharLen = m_chars.size() - utf8Start; return m_chars.substr(utf8Start, utf8CharLen); } - void TextEditor::Line::setNeedsUpdate(bool needsUpdate) { + void Line::setNeedsUpdate(bool needsUpdate) { m_colorized = m_colorized && !needsUpdate; } - void TextEditor::Line::append(const char *text) { + void Line::append(const char *text) { append(std::string(text)); } - void TextEditor::Line::append(const char text) { + void Line::append(const char text) { append(std::string(1, text)); } - void TextEditor::Line::append(const std::string &text) { + void Line::append(const std::string &text) { Line line(text); append(line); } - void TextEditor::Line::append(const Line &line) { + void Line::append(const Line &line) { append(line.begin(), line.end()); } - void TextEditor::Line::append(LineIterator begin, LineIterator end) { + void Line::append(LineIterator begin, LineIterator end) { if (begin.m_charsIter < end.m_charsIter) { m_chars.append(begin.m_charsIter, end.m_charsIter); - m_lineTextSize = -1; + m_lineMaxColumn = -1; } if (begin.m_colorsIter < end.m_colorsIter) m_colors.append(begin.m_colorsIter, end.m_colorsIter); @@ -283,24 +378,24 @@ namespace hex::ui { m_colorized = false; } - void TextEditor::Line::insert(LineIterator iter, const std::string &text) { + void Line::insert(LineIterator iter, const std::string &text) { insert(iter, text.begin(), text.end()); } - void TextEditor::Line::insert(LineIterator iter, const char text) { + void Line::insert(LineIterator iter, const char text) { insert(iter, std::string(1, text)); } - void TextEditor::Line::insert(LineIterator iter, strConstIter beginString, strConstIter endString) { + void Line::insert(LineIterator iter, strConstIter beginString, strConstIter endString) { Line line(std::string(beginString, endString)); insert(iter, line); } - void TextEditor::Line::insert(LineIterator iter, const Line &line) { + void Line::insert(LineIterator iter, const Line &line) { insert(iter, line.begin(), line.end()); } - void TextEditor::Line::insert(LineIterator iter, LineIterator beginLine, LineIterator endLine) { + void Line::insert(LineIterator iter, LineIterator beginLine, LineIterator endLine) { if (iter == end()) append(beginLine, endLine); else { @@ -308,19 +403,19 @@ namespace hex::ui { m_colors.insert(iter.m_colorsIter, beginLine.m_colorsIter, endLine.m_colorsIter); m_flags.insert(iter.m_flagsIter, beginLine.m_flagsIter, endLine.m_flagsIter); m_colorized = false; - m_lineTextSize = -1; + m_lineMaxColumn = -1; } } - void TextEditor::Line::erase(LineIterator begin) { + void Line::erase(LineIterator begin) { m_chars.erase(begin.m_charsIter); m_colors.erase(begin.m_colorsIter); m_flags.erase(begin.m_flagsIter); m_colorized = false; - m_lineTextSize = -1; + m_lineMaxColumn = -1; } - void TextEditor::Line::erase(LineIterator begin, u64 count) { + void Line::erase(LineIterator begin, u64 count) { if (count == (u64) -1) count = m_chars.size() - (begin.m_charsIter - m_chars.begin()); else @@ -329,85 +424,113 @@ namespace hex::ui { m_colors.erase(begin.m_colorsIter, begin.m_colorsIter + count); m_flags.erase(begin.m_flagsIter, begin.m_flagsIter + count); m_colorized = false; - m_lineTextSize = -1; + m_lineMaxColumn = -1; } - void TextEditor::Line::erase(u64 start, u64 length) { - if (length == (u64) -1 || start + length >= m_chars.size()) - length = m_chars.size() - start; + void Line::erase(u64 start, i64 length) { + if (m_chars.empty() || start >= (u64) maxColumn()) + return; + if (length < 0 || start >= (u64) maxColumn() - length) + length = maxColumn() - start; u64 utf8Start = 0; for (u64 utf8Index = 0; utf8Index < start; ++utf8Index) { utf8Start += TextEditor::utf8CharLength(m_chars[utf8Start]); } u64 utf8Length = 0; - for (u64 utf8Index = 0; utf8Index < length; ++utf8Index) { + for (i64 utf8Index = 0; utf8Index < length; ++utf8Index) { utf8Length += TextEditor::utf8CharLength(m_chars[utf8Start + utf8Length]); } utf8Length = std::min(utf8Length, (u64) (m_chars.size() - utf8Start)); erase(begin() + utf8Start, utf8Length); } - void TextEditor::Line::clear() { + void Line::clear() { m_chars.clear(); m_colors.clear(); m_flags.clear(); m_colorized = false; - m_lineTextSize = -1; + m_lineMaxColumn = -1; } - void TextEditor::Line::setLine(const std::string &text) { + void Line::setLine(const std::string &text) { m_chars = text; m_colors = std::string(text.size(), 0x00); m_flags = std::string(text.size(), 0x00); m_colorized = false; - m_lineTextSize = -1; + m_lineMaxColumn = -1; } - void TextEditor::Line::setLine(const Line &text) { + void Line::setLine(const Line &text) { m_chars = text.m_chars; m_colors = text.m_colors; m_flags = text.m_flags; m_colorized = text.m_colorized; - m_lineTextSize = text.m_lineTextSize; + m_lineMaxColumn = text.m_lineMaxColumn; } - bool TextEditor::Line::needsUpdate() const { + bool Line::needsUpdate() const { return !m_colorized; } - TextEditor *TextEditor::GetSourceCodeEditor() { + bool TextEditor::ActionableBox::trigger() { + auto mousePos = ImGui::GetMousePos(); + if (mousePos.x <= m_box.Min.x || mousePos.x >= m_box.Max.x || + mousePos.y < m_box.Min.y || mousePos.y > m_box.Max.y) + return false; + return true; + } + + void TextEditor::ActionableBox::shiftBoxVertically(float lineCount, float lineHeight) { + m_box.Min.y += lineCount * lineHeight; + m_box.Max.y += lineCount * lineHeight; + } + + void TextEditor::ErrorHoverBox::callback() { + ImGui::BeginTooltip(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f)); + ImGui::Text("Error at line %d:", m_pos.m_line); + ImGui::PopStyleColor(); + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.2f, 1.0f)); + ImGui::TextUnformatted(m_errorText.c_str()); + ImGui::PopStyleColor(); + ImGui::EndTooltip(); + } + + TextEditor *TextEditor::getSourceCodeEditor() { if (m_sourceCodeEditor != nullptr) return m_sourceCodeEditor; return this; } bool TextEditor::isEmpty() const { - if (m_lines.empty()) - return true; - if (m_lines.size() == 1) { - if (m_lines[0].empty()) - return true; - if (m_lines[0].size() == 1 && m_lines[0].front() == '\n') - return true; + auto size = m_lines.size(); + for (u32 i = 0; i < size; ++i) { + if (!m_lines[i].empty()) + return false; } - return false; + return true; } - void TextEditor::setSelection(const Selection &selection) { + bool TextEditor::EditorState::operator==(const EditorState &o) const { + return m_selection == o.m_selection && m_cursorPosition == o.m_cursorPosition; + } + + void TextEditor::setSelection(const Range &selection) { m_state.m_selection = setCoordinates(selection); } - TextEditor::Selection TextEditor::getSelection() const { + Range TextEditor::getSelection() const { return m_state.m_selection; } void TextEditor::selectWordUnderCursor() { auto wordStart = findWordStart(getCursorPosition()); - setSelection(Selection(wordStart, findWordEnd(wordStart))); + setSelection(Range(wordStart, findWordEnd(wordStart))); } void TextEditor::selectAll() { - setSelection(Selection(setCoordinates(0, 0), setCoordinates(-1, -1))); + setSelection(Range(Coordinates(this, 0, 0), Coordinates(this, -1, -1))); } bool TextEditor::hasSelection() const { @@ -424,15 +547,16 @@ namespace hex::ui { } TextEditor::PaletteIndex TextEditor::getColorIndexFromFlags(Line::Flags flags) { - if (flags.m_bits.globalDocComment) + auto commentBits = flags.m_value & inComment; + if (commentBits == (i32) Line::Comments::Global) return PaletteIndex::GlobalDocComment; - if (flags.m_bits.blockDocComment) + if (commentBits == (i32) Line::Comments::BlockDoc) return PaletteIndex::DocBlockComment; - if (flags.m_bits.docComment) + if (commentBits == (i32) Line::Comments::Doc) return PaletteIndex::DocComment; - if (flags.m_bits.blockComment) + if (commentBits == (i32) Line::Comments::Block) return PaletteIndex::BlockComment; - if (flags.m_bits.comment) + if (commentBits == (i32) Line::Comments::Line) return PaletteIndex::Comment; if (flags.m_bits.deactivated) return PaletteIndex::PreprocessorDeactivated; @@ -498,7 +622,7 @@ namespace hex::ui { m_state.m_cursorPosition = screenPosToCoordinates(ImGui::GetMousePos()); auto line = m_state.m_cursorPosition.m_line; m_state.m_selection.m_start = setCoordinates(line, 0); - m_state.m_selection.m_end = setCoordinates(line, getLineMaxCharColumn(line)); + m_state.m_selection.m_end = setCoordinates(line, lineMaxColumn(line)); } m_lastClick = -1.0f; @@ -565,7 +689,7 @@ namespace hex::ui { } // the index here is array index so zero based - void TextEditor::FindReplaceHandler::selectFound(TextEditor *editor, i32 found) { + void FindReplaceHandler::selectFound(TextEditor *editor, i32 found) { if (found < 0 || found >= (i64) m_matches.size()) return; editor->setSelection(m_matches[found].m_selection); @@ -574,7 +698,7 @@ namespace hex::ui { // The returned index is shown in the form // 'index of count' so 1 based - u32 TextEditor::FindReplaceHandler::findMatch(TextEditor *editor, i32 index) { + u32 FindReplaceHandler::findMatch(TextEditor *editor, i32 index) { if (editor->m_textChanged || m_optionsChanged) { std::string findWord = getFindWord(); @@ -632,7 +756,7 @@ namespace hex::ui { } // returns 1 based index - u32 TextEditor::FindReplaceHandler::findPosition(TextEditor *editor, Coordinates pos, bool isNext) { + u32 FindReplaceHandler::findPosition(TextEditor *editor, Coordinates pos, bool isNext) { if (editor->m_textChanged || m_optionsChanged) { std::string findWord = getFindWord(); if (findWord.empty()) @@ -646,14 +770,14 @@ namespace hex::ui { return 0; if (isNext) { for (i32 i = 0; i < count; i++) { - auto interval = Selection(m_matches[i==0 ? count-1 : i - 1].m_selection.m_end,m_matches[i].m_selection.m_end); + auto interval = Range(m_matches[i == 0 ? count - 1 : i - 1].m_selection.m_end,m_matches[i].m_selection.m_end); if (interval.contains(pos)) return i + 1; } } else { for (i32 i = 0; i < count; i++) { - auto interval = Selection(m_matches[i == 0 ? count - 1 : i - 1].m_selection.m_start, m_matches[i].m_selection.m_start); - if (interval.contains(pos, 2)) + auto interval = Range(m_matches[i == 0 ? count - 1 : i - 1].m_selection.m_start,m_matches[i].m_selection.m_start); + if (interval.contains(pos, Range::EndsInclusive::Start)) return i == 0 ? count : i; } } @@ -681,10 +805,10 @@ namespace hex::ui { } // Performs actual search to fill mMatches - bool TextEditor::FindReplaceHandler::findNext(TextEditor *editor) { + bool FindReplaceHandler::findNext(TextEditor *editor) { Coordinates curPos = m_matches.empty() ? editor->m_state.m_cursorPosition : editor->lineCoordsToIndexCoords(m_matches.back().m_cursorPosition); - u64 matchLength = getStringCharacterCount(m_findWord); + u64 matchLength = stringCharacterCount(m_findWord); u64 matchBytes = m_findWord.size(); u64 byteIndex = 0; @@ -757,13 +881,13 @@ namespace hex::ui { if (textLoc == std::string::npos) return false; TextEditor::EditorState state; - state.m_selection = Selection(TextEditor::stringIndexToCoordinates(textLoc, textSrc), TextEditor::stringIndexToCoordinates(textLoc + matchBytes, textSrc)); + state.m_selection = Range(TextEditor::stringIndexToCoordinates(textLoc, textSrc), TextEditor::stringIndexToCoordinates(textLoc + matchBytes, textSrc)); state.m_cursorPosition = state.m_selection.m_end; m_matches.push_back(state); return true; } - void TextEditor::FindReplaceHandler::findAllMatches(TextEditor *editor, std::string findWord) { + void FindReplaceHandler::findAllMatches(TextEditor *editor, std::string findWord) { if (findWord.empty()) { editor->ensureCursorVisible(); @@ -809,7 +933,7 @@ namespace hex::ui { } - bool TextEditor::FindReplaceHandler::replace(TextEditor *editor, bool right) { + bool FindReplaceHandler::replace(TextEditor *editor, bool right) { if (m_matches.empty() || m_findWord == m_replaceWord || m_findWord.empty()) return false; @@ -819,7 +943,7 @@ namespace hex::ui { editor->m_state.m_cursorPosition = editor->m_state.m_selection.m_start; if (editor->isStartOfLine()) { editor->m_state.m_cursorPosition.m_line--; - editor->m_state.m_cursorPosition.m_column = editor->getLineMaxCharColumn(editor->m_state.m_cursorPosition.m_line); + editor->m_state.m_cursorPosition.m_column = editor->lineMaxColumn(editor->m_state.m_cursorPosition.m_line); } else editor->m_state.m_cursorPosition.m_column--; } @@ -829,7 +953,7 @@ namespace hex::ui { UndoRecord u; u.m_before = editor->m_state; u.m_removed = editor->getSelectedText(); - u.m_removedSelection = editor->m_state.m_selection; + u.m_removedRange = editor->m_state.m_selection; editor->deleteSelection(); if (getFindRegEx()) { std::string replacedText = std::regex_replace(editor->getText(), std::regex(m_findWord), m_replaceWord, std::regex_constants::format_first_only | std::regex_constants::format_no_copy); @@ -837,12 +961,12 @@ namespace hex::ui { } else u.m_added = m_replaceWord; - u.m_addedSelection.m_start = editor->setCoordinates(editor->m_state.m_cursorPosition); + u.m_addedRange.m_start = editor->setCoordinates(editor->m_state.m_cursorPosition); editor->insertText(u.m_added); editor->setCursorPosition(editor->m_state.m_selection.m_end); - u.m_addedSelection.m_end = editor->setCoordinates(editor->m_state.m_cursorPosition); + u.m_addedRange.m_end = editor->setCoordinates(editor->m_state.m_cursorPosition); editor->ensureCursorVisible(); ImGui::SetKeyboardFocusHere(0); @@ -857,7 +981,7 @@ namespace hex::ui { return false; } - bool TextEditor::FindReplaceHandler::replaceAll(TextEditor *editor) { + bool FindReplaceHandler::replaceAll(TextEditor *editor) { u32 count = m_matches.size(); for (u32 i = 0; i < count; i++) diff --git a/plugins/ui/source/ui/text_editor/utf8.cpp b/plugins/ui/source/ui/text_editor/utf8.cpp index dfc9ae54e..5619c36a0 100644 --- a/plugins/ui/source/ui/text_editor/utf8.cpp +++ b/plugins/ui/source/ui/text_editor/utf8.cpp @@ -1,10 +1,29 @@ #include #include +#include +#include #include namespace hex::ui { - i32 TextEditor::Line::getColumnIndex(i32 column) const { + TextEditor::Line TextEditor::Line::trim(TrimMode trimMode) { + if (m_chars.empty()) + return m_emptyLine; + std::string trimmed = wolv::util::trim(m_chars); + auto idx = m_chars.find(trimmed); + if (idx == std::string::npos) + return m_emptyLine; + if (trimMode == TrimMode::TrimNone) + return *this; + else if (trimMode == TrimMode::TrimEnd) + return subLine(0, idx + trimmed.size()); + else if (trimMode == TrimMode::TrimStart) + return subLine(idx, size() - idx); + else + return subLine(idx, trimmed.size()); + } + + i32 TextEditor::Line::columnIndex(i32 column) const { i32 idx = 0; for (i32 col = 0; idx < (i32) size() && col < column; ++col) @@ -13,7 +32,20 @@ namespace hex::ui { return idx; } - i32 TextEditor::Line::getCharColumn(i32 stringIndex) const { + i32 TextEditor::Line::maxColumn() { + if (m_lineMaxColumn > 0) + return m_lineMaxColumn; + m_lineMaxColumn = indexColumn((i32) size()); + return m_lineMaxColumn; + } + + i32 TextEditor::Line::maxColumn() const { + if (m_lineMaxColumn > 0) + return m_lineMaxColumn; + return indexColumn((i32) size()); + } + + i32 TextEditor::Line::indexColumn(i32 stringIndex) const { i32 limit = std::max(0, std::min(stringIndex, (i32) size())); i32 col = 0; @@ -23,13 +55,44 @@ namespace hex::ui { return col; } - i32 TextEditor::Line::getStringTextSize(const std::string &str) const { + i32 TextEditor::Line::stringTextSize(const std::string &str) const { + i32 result = 0; + if (str.empty()) + return 0; + if (ImGui::GetFont() == nullptr) { + fonts::CodeEditor().push(); + result = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, str.c_str(), nullptr, nullptr).x; + fonts::CodeEditor().pop(); + return result; + } return ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, str.c_str(), nullptr, nullptr).x; } - i32 TextEditor::Line::getLineTextSize() { - m_lineTextSize = getStringTextSize(m_chars); - return m_lineTextSize; + i32 TextEditor::Line::textSize(u32 index) const { + if (m_chars.empty()) + return 0; + return stringTextSize(m_chars.substr(0, index)); + } + + i32 TextEditor::Line::textSize() const { + if (m_chars.empty()) + return 0; + return stringTextSize(m_chars); + } + + i32 TextEditor::Line::lineTextSize(TrimMode trimMode) { + auto trimmedLine = trim(trimMode); + return trimmedLine.textSize(); + } + + i32 TextEditor::Line::textSizeIndex(float textSize, i32 position) { + i32 result = textSize / ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; + auto currentSize = stringTextSize(m_chars.substr(position, result)); + while (currentSize < textSize && (u32)(position + result) < size()) { + result += TextEditor::utf8CharLength(m_chars[position + result]); + currentSize = stringTextSize(m_chars.substr(position, result)); + } + return result; } // https://en.wikipedia.org/wiki/UTF-8 @@ -48,7 +111,7 @@ namespace hex::ui { return 1; } - i32 TextEditor::getStringCharacterCount(const std::string &str) { + i32 TextEditor::stringCharacterCount(const std::string &str) { if (str.empty()) return 0; i32 count = 0; @@ -57,18 +120,18 @@ namespace hex::ui { return count; } - i32 TextEditor::getLineCharColumn(i32 lineIndex, i32 stringIndex) { + i32 TextEditor::lineIndexColumn(i32 lineIndex, i32 stringIndex) { if (lineIndex >= (i64) m_lines.size() || lineIndex < 0) return 0; Line &line = m_lines[lineIndex]; - return line.getCharColumn(stringIndex); + return line.indexColumn(stringIndex); } - i32 TextEditor::getLineMaxCharColumn(i32 lineIndex) { + i32 TextEditor::lineMaxColumn(i32 lineIndex) { if (lineIndex >= (i64) m_lines.size() || lineIndex < 0) return 0; - Line &line = m_lines[lineIndex]; - return line.getCharColumn(line.size()); + + return m_lines[lineIndex].maxColumn(); } // "Borrowed" from ImGui source @@ -142,7 +205,7 @@ namespace hex::ui { return Invalid; const auto &line = m_lines[coordinates.m_line]; - return Coordinates(coordinates.m_line,line.getColumnIndex(coordinates.m_column)); + return Coordinates(coordinates.m_line,line.columnIndex(coordinates.m_column)); } i32 TextEditor::lineCoordinatesToIndex(const Coordinates &coordinates) const { @@ -150,14 +213,14 @@ namespace hex::ui { return -1; const auto &line = m_lines[coordinates.m_line]; - return line.getColumnIndex(coordinates.m_column); + return line.columnIndex(coordinates.m_column); } TextEditor::Coordinates TextEditor::getCharacterCoordinates(i32 lineIndex, i32 strIndex) { if (lineIndex < 0 || lineIndex >= (i32) m_lines.size()) return Coordinates(0, 0); auto &line = m_lines[lineIndex]; - return setCoordinates(lineIndex, line.getCharColumn(strIndex)); + return setCoordinates(lineIndex, line.indexColumn(strIndex)); } u64 TextEditor::getLineByteCount(i32 lineIndex) const { @@ -175,7 +238,7 @@ namespace hex::ui { auto line = std::count(str.begin(), str.end(), '\n'); auto index = str.find_last_of('\n'); str = str.substr(index + 1); - auto col = TextEditor::getStringCharacterCount(str); + auto col = TextEditor::stringCharacterCount(str); return TextEditor::Coordinates(line, col); }