From df1e97af0a82105739d2040207e39956f03822bf Mon Sep 17 00:00:00 2001 From: paxcut <53811119+paxcut@users.noreply.github.com> Date: Fri, 8 Aug 2025 12:47:52 -0700 Subject: [PATCH] improv: further reformatting of text editor in an effort to bring it into the ui plugin. (#2385) Added the ui namespace and broke the main rendering function into a set of smaller functions. Reorganized the header code separating functions into rough groups that eventually will be in separate files. --- .../ColorTextEditor/include/TextEditor.h | 1914 ++--- .../ColorTextEditor/source/TextEditor.cpp | 7307 +++++++++-------- .../text_highlighting/pattern_language.hpp | 12 +- .../content/views/view_pattern_editor.hpp | 24 +- .../text_highlighting/pattern_language.cpp | 86 +- plugins/builtin/source/content/themes.cpp | 96 +- .../source/content/tools/demangler.cpp | 6 +- .../content/views/view_pattern_editor.cpp | 94 +- 8 files changed, 4826 insertions(+), 4713 deletions(-) diff --git a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h index 028d01f76..f882c04c7 100644 --- a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h +++ b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h @@ -13,404 +13,613 @@ #include #include "imgui.h" #include "imgui_internal.h" -using strConstIter = std::string::const_iterator; -int32_t utf8CharLength(uint8_t c); -int32_t getStringCharacterCount(const std::string& str); -class TextEditor -{ -public: - 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 - }; - // 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 - { - int32_t m_line, m_column; - Coordinates() : m_line(0), m_column(0) {} - Coordinates(int32_t line, int32_t column) : m_line(line), m_column(column) {} +namespace hex::ui { + using strConstIter = std::string::const_iterator; + int32_t utf8CharLength(uint8_t c); + int32_t getStringCharacterCount(const std::string& str); - bool operator ==(const Coordinates& o) const - { - return - m_line == o.m_line && - m_column == o.m_column; - } + class TextEditor { - bool operator !=(const Coordinates& o) const - { - return - m_line != o.m_line || - m_column != o.m_column; - } - - bool operator <(const Coordinates& o) const - { - if (m_line != o.m_line) - return m_line < o.m_line; - return m_column < o.m_column; - } - - bool operator >(const Coordinates& o) const - { - if (m_line != o.m_line) - return m_line > o.m_line; - return m_column > o.m_column; - } - - bool operator <=(const Coordinates& o) const - { - if (m_line != o.m_line) - return m_line < o.m_line; - return m_column <= o.m_column; - } - - bool operator >=(const Coordinates& o) const - { - if (m_line != o.m_line) - return m_line > o.m_line; - return m_column >= o.m_column; - } - - Coordinates operator +(const Coordinates & o) const { - return Coordinates(m_line + o.m_line, m_column + o.m_column); - } - Coordinates operator -(const Coordinates & o) const { - return Coordinates(m_line - o.m_line, m_column - o.m_column); - } - }; - inline static const Coordinates Invalid = Coordinates(0x80000000,0x80000000); - - 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 ; - - class ActionableBox { - - ImRect m_box; public: - ActionableBox()=default; - explicit ActionableBox(const ImRect &box) : m_box(box) {} - virtual bool trigger() { - return ImGui::IsMouseHoveringRect(m_box.Min, m_box.Max); - } + // 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 { + int32_t m_line, m_column; - virtual void callback() {} - }; + Coordinates() : m_line(0), m_column(0) {} + Coordinates(int32_t line, int32_t column) : m_line(line), m_column(column) {} - class CursorChangeBox : public ActionableBox { - public: - CursorChangeBox()=default; - explicit CursorChangeBox(const ImRect &box) : ActionableBox(box) { - - } - - 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); - } - - private: - TextEditor *m_editor; - }; - - 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(); - } - }; - using ErrorHoverBoxes = std::map; - - // 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 Line { - public: - struct FlagBits { - bool comment : 1; - bool blockComment : 1; - bool docComment : 1; - bool blockDocComment : 1; - bool globalDocComment : 1; - bool deactivated : 1; - bool preprocessor : 1; - bool matchedBracket : 1; - }; - union Flags { - Flags(char value) : m_value(value) {} - Flags(FlagBits bits) : m_bits(bits) {} - FlagBits m_bits; - char m_value; - }; - constexpr static char inComment = 31; - - int32_t getCharacterColumn(int32_t index) const; - - 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*() { - return *m_charsIter; + bool operator==(const Coordinates &o) const { + return + m_line == o.m_line && + m_column == o.m_column; } - LineIterator operator++() { - LineIterator iter = *this; - ++iter.m_charsIter; - ++iter.m_colorsIter; - ++iter.m_flagsIter; + bool operator!=(const Coordinates &o) const { + return + m_line != o.m_line || + m_column != o.m_column; + } + + bool operator<(const Coordinates &o) const { + if (m_line != o.m_line) + return m_line < o.m_line; + return m_column < o.m_column; + } + + bool operator>(const Coordinates &o) const { + if (m_line != o.m_line) + return m_line > o.m_line; + return m_column > o.m_column; + } + + bool operator<=(const Coordinates &o) const { + if (m_line != o.m_line) + return m_line < o.m_line; + return m_column <= o.m_column; + } + + bool operator>=(const Coordinates &o) const { + if (m_line != o.m_line) + return m_line > o.m_line; + return m_column >= o.m_column; + } + + Coordinates operator+(const Coordinates &o) const { + return Coordinates(m_line + o.m_line, m_column + o.m_column); + } + + Coordinates operator-(const Coordinates &o) const { + return Coordinates(m_line - o.m_line, m_column - o.m_column); + } + }; + + inline static const Coordinates Invalid = Coordinates(0x80000000, 0x80000000); + + struct Selection { + Coordinates m_start; + Coordinates 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() { + return Coordinates(m_start.m_line, m_end.m_line); + } + + Coordinates 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 isSingleLine() { + return m_start.m_line == m_end.m_line; + } + }; + + struct EditorState { + Selection m_selection; + Coordinates m_cursorPosition; + }; + + class FindReplaceHandler { + public: + FindReplaceHandler(); + typedef std::vector Matches; + Matches &getMatches() { return m_matches; } + bool findNext(TextEditor *editor); + uint32_t findMatch(TextEditor *editor, bool isNex); + bool replace(TextEditor *editor, bool right); + bool replaceAll(TextEditor *editor); + std::string &getFindWord() { return m_findWord; } + + void setFindWord(TextEditor *editor, const std::string &findWord) { + if (findWord != m_findWord) { + findAllMatches(editor, findWord); + m_findWord = findWord; + } + } + + std::string &getReplaceWord() { return m_replaceWord; } + void setReplaceWord(const std::string &replaceWord) { m_replaceWord = replaceWord; } + void selectFound(TextEditor *editor, int32_t found); + void findAllMatches(TextEditor *editor, std::string findWord); + uint32_t findPosition(TextEditor *editor, Coordinates pos, bool isNext); + bool getMatchCase() const { return m_matchCase; } + + void setMatchCase(TextEditor *editor, bool matchCase) { + if (matchCase != m_matchCase) { + m_matchCase = matchCase; + m_optionsChanged = true; + findAllMatches(editor, m_findWord); + } + } + + bool getWholeWord() const { return m_wholeWord; } + void setWholeWord(TextEditor *editor, bool wholeWord) { + if (wholeWord != m_wholeWord) { + m_wholeWord = wholeWord; + m_optionsChanged = true; + findAllMatches(editor, m_findWord); + } + } + + bool getFindRegEx() const { return m_findRegEx; } + void setFindRegEx(TextEditor *editor, bool findRegEx) { + if (findRegEx != m_findRegEx) { + m_findRegEx = findRegEx; + m_optionsChanged = true; + findAllMatches(editor, m_findWord); + } + } + + void resetMatches() { + m_matches.clear(); + m_findWord = ""; + } + + private: + std::string m_findWord; + std::string m_replaceWord; + bool m_matchCase; + bool m_wholeWord; + bool m_findRegEx; + bool m_optionsChanged; + Matches m_matches; + }; + + 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 + }; + + + 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; + + class ActionableBox { + + ImRect m_box; + public: + ActionableBox() = default; + explicit ActionableBox(const ImRect &box) : m_box(box) {} + + virtual bool trigger() { + return ImGui::IsMouseHoveringRect(m_box.Min, m_box.Max); + } + + virtual void callback() {} + }; + + class CursorChangeBox : public ActionableBox { + public: + CursorChangeBox() = default; + explicit CursorChangeBox(const ImRect &box) : ActionableBox(box) {} + + 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); + } + + private: + TextEditor *m_editor; + }; + + 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(); + } + }; + + using ErrorHoverBoxes = std::map; + + // 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 Line { + public: + struct FlagBits { + bool comment: 1; + bool blockComment: 1; + bool docComment: 1; + bool blockDocComment: 1; + bool globalDocComment: 1; + bool deactivated: 1; + bool preprocessor: 1; + bool matchedBracket: 1; + }; + + union Flags { + Flags(char value) : m_value(value) {} + Flags(FlagBits bits) : m_bits(bits) {} + FlagBits m_bits; + char m_value; + }; + + constexpr static char inComment = 31; + + 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*() { + return *m_charsIter; + } + + LineIterator operator++() { + LineIterator iter = *this; + ++iter.m_charsIter; + ++iter.m_colorsIter; + ++iter.m_flagsIter; + return iter; + } + + LineIterator operator=(const LineIterator &other) { + m_charsIter = other.m_charsIter; + m_colorsIter = other.m_colorsIter; + m_flagsIter = other.m_flagsIter; + return *this; + } + + bool operator!=(const LineIterator &other) const { + return m_charsIter != other.m_charsIter || m_colorsIter != other.m_colorsIter || + m_flagsIter != other.m_flagsIter; + } + + bool operator==(const LineIterator &other) const { + return m_charsIter == other.m_charsIter && m_colorsIter == other.m_colorsIter && + m_flagsIter == other.m_flagsIter; + } + + LineIterator operator+(int32_t n) { + LineIterator iter = *this; + iter.m_charsIter += n; + iter.m_colorsIter += n; + iter.m_flagsIter += n; + return iter; + } + + int32_t operator-(LineIterator l) { + return m_charsIter - l.m_charsIter; + } + }; + + LineIterator begin() const { + LineIterator iter; + iter.m_charsIter = m_chars.begin(); + iter.m_colorsIter = m_colors.begin(); + iter.m_flagsIter = m_flags.begin(); return iter; } - LineIterator operator=(const LineIterator &other) { - m_charsIter = other.m_charsIter; - m_colorsIter = other.m_colorsIter; - m_flagsIter = other.m_flagsIter; + LineIterator end() const { + LineIterator iter; + iter.m_charsIter = m_chars.end(); + iter.m_colorsIter = m_colors.end(); + iter.m_flagsIter = m_flags.end(); + return iter; + } + + std::string m_chars; + std::string m_colors; + std::string m_flags; + bool m_colorized = false; + + Line() : m_chars(), m_colors(), m_flags(), m_colorized(false) {} + 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) {} + Line(const Line &line) : m_chars(line.m_chars), m_colors(line.m_colors), m_flags(line.m_flags), m_colorized(line.m_colorized) {} + + int32_t getCharacterColumn(int32_t index) const; + LineIterator begin() { + LineIterator iter; + iter.m_charsIter = m_chars.begin(); + iter.m_colorsIter = m_colors.begin(); + iter.m_flagsIter = m_flags.begin(); + return iter; + } + + LineIterator end() { + LineIterator iter; + iter.m_charsIter = m_chars.end(); + iter.m_colorsIter = m_colors.end(); + iter.m_flagsIter = m_flags.end(); + return iter; + } + + 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; return *this; } - bool operator!=(const LineIterator &other) const { - return m_charsIter != other.m_charsIter || m_colorsIter != other.m_colorsIter || m_flagsIter != other.m_flagsIter; + 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; + return *this; } - bool operator==(const LineIterator &other) const { - return m_charsIter == other.m_charsIter && m_colorsIter == other.m_colorsIter && m_flagsIter == other.m_flagsIter; + uint64_t size() const { + return m_chars.size(); } - LineIterator operator+(int32_t n) { - LineIterator iter = *this; - iter.m_charsIter += n; - iter.m_colorsIter += n; - iter.m_flagsIter += n; - return iter; + enum class LinePart { + Chars, + Utf8, + Colors, + Flags + }; + + char front(LinePart part = LinePart::Chars) const { + if (part == LinePart::Chars && !m_chars.empty()) + return m_chars.front(); + if (part == LinePart::Colors && !m_colors.empty()) + return m_colors.front(); + if (part == LinePart::Flags && !m_flags.empty()) + return m_flags.front(); + return 0x00; } - int32_t operator-(LineIterator l) { - return m_charsIter - l.m_charsIter; - } - }; - - LineIterator begin() const { - LineIterator iter; - iter.m_charsIter = m_chars.begin(); - iter.m_colorsIter = m_colors.begin(); - iter.m_flagsIter = m_flags.begin(); - return iter; - } - - LineIterator end() const { - LineIterator iter; - iter.m_charsIter = m_chars.end(); - iter.m_colorsIter = m_colors.end(); - iter.m_flagsIter = m_flags.end(); - return iter; - } - - std::string m_chars; - std::string m_colors; - std::string m_flags; - bool m_colorized = false; - Line() : m_chars(), m_colors(), m_flags(), m_colorized(false) {} - - 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) {} - Line(const Line &line) : m_chars(line.m_chars), m_colors(line.m_colors), m_flags(line.m_flags), m_colorized(line.m_colorized) {} - - LineIterator begin() { - LineIterator iter; - iter.m_charsIter = m_chars.begin(); - iter.m_colorsIter = m_colors.begin(); - iter.m_flagsIter = m_flags.begin(); - return iter; - } - - LineIterator end() { - LineIterator iter; - iter.m_charsIter = m_chars.end(); - iter.m_colorsIter = m_colors.end(); - iter.m_flagsIter = m_flags.end(); - return iter; - } - - 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; - return *this; - } - - 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; - return *this; - } - - uint64_t size() const { - return m_chars.size(); - } - enum class LinePart { - Chars, - Utf8, - Colors, - Flags - }; - - char front(LinePart part = LinePart::Chars) const { - if (part == LinePart::Chars && !m_chars.empty()) - return m_chars.front(); - if (part == LinePart::Colors && !m_colors.empty()) - return m_colors.front(); - if (part == LinePart::Flags && !m_flags.empty()) - return m_flags.front(); - return 0x00; - } - - std::string frontUtf8(LinePart part = LinePart::Chars) const { - if (part == LinePart::Chars && !m_chars.empty()) - return m_chars.substr(0, utf8CharLength(m_chars[0])); - if (part == LinePart::Colors && !m_colors.empty()) - return m_colors.substr(0, utf8CharLength(m_chars[0])); - if (part == LinePart::Flags && !m_flags.empty()) - return m_flags.substr(0, utf8CharLength(m_chars[0])); - return ""; - } - - void push_back(char c) { - m_chars.push_back(c); - m_colors.push_back(0x00); - m_flags.push_back(0x00); - m_colorized = false; - } - - bool empty() const { - return m_chars.empty(); - } - - std::string substr(uint64_t start, uint64_t length = (uint64_t)-1, LinePart part = LinePart::Chars ) const { - if (start >= m_chars.size() || m_colors.size() != m_chars.size() || m_flags.size() != m_chars.size()) - return ""; - if (length == (uint64_t)-1 || start + length >= m_chars.size()) - length = m_chars.size() - start; - if (length == 0) + std::string frontUtf8(LinePart part = LinePart::Chars) const { + if (part == LinePart::Chars && !m_chars.empty()) + return m_chars.substr(0, utf8CharLength(m_chars[0])); + if (part == LinePart::Colors && !m_colors.empty()) + return m_colors.substr(0, utf8CharLength(m_chars[0])); + if (part == LinePart::Flags && !m_flags.empty()) + return m_flags.substr(0, utf8CharLength(m_chars[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) { - uint64_t utf8Start= 0; + void push_back(char c) { + m_chars.push_back(c); + m_colors.push_back(0x00); + m_flags.push_back(0x00); + m_colorized = false; + } + + bool empty() const { + return m_chars.empty(); + } + + std::string substr(uint64_t start, uint64_t length = (uint64_t) -1, LinePart part = LinePart::Chars) const { + if (start >= m_chars.size() || m_colors.size() != m_chars.size() || m_flags.size() != m_chars.size()) + return ""; + if (length == (uint64_t) -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); + if (part == LinePart::Utf8) { + uint64_t utf8Start = 0; + for (uint64_t utf8Index = 0; utf8Index < start; ++utf8Index) { + utf8Start += utf8CharLength(m_chars[utf8Start]); + } + uint64_t utf8Length = 0; + for (uint64_t utf8Index = 0; utf8Index < length; ++utf8Index) { + utf8Length += utf8CharLength(m_chars[utf8Start + utf8Length]); + } + return m_chars.substr(utf8Start, utf8Length); + } + return ""; + } + + char operator[](uint64_t index) const { + index = std::clamp(index, (uint64_t) 0, (uint64_t) (m_chars.size() - 1)); + return m_chars[index]; + } + + // C++ can't overload functions based on return type, so use any type other + // than uint64_t to avoid ambiguity. + template + std::string operator[](T column) const { + uint64_t utf8Length = getStringCharacterCount(m_chars); + uint64_t index = static_cast(column); + index = std::clamp(index, (uint64_t) 0, utf8Length - 1); + uint64_t utf8Start = 0; + for (uint64_t utf8Index = 0; utf8Index < index; ++utf8Index) { + utf8Start += utf8CharLength(m_chars[utf8Start]); + } + uint64_t utf8CharLen = utf8CharLength(m_chars[utf8Start]); + if (utf8Start + utf8CharLen > m_chars.size()) + utf8CharLen = m_chars.size() - utf8Start; + return m_chars.substr(utf8Start, utf8CharLen); + } + + void setNeedsUpdate(bool needsUpdate) { + m_colorized = m_colorized && !needsUpdate; + } + + void append(const char *text) { + append(std::string(text)); + } + + void append(const char text) { + append(std::string(1, text)); + } + + void append(const std::string &text) { + Line line(text); + append(line); + } + + void append(const Line &line) { + append(line.begin(), line.end()); + } + + void append(LineIterator begin, LineIterator end) { + if (begin.m_charsIter < end.m_charsIter) + m_chars.append(begin.m_charsIter, end.m_charsIter); + if (begin.m_colorsIter < end.m_colorsIter) + m_colors.append(begin.m_colorsIter, end.m_colorsIter); + if (begin.m_flagsIter < end.m_flagsIter) + m_flags.append(begin.m_flagsIter, end.m_flagsIter); + m_colorized = false; + } + + void insert(LineIterator iter, const std::string &text) { + insert(iter, text.begin(), text.end()); + } + + void insert(LineIterator iter, const char text) { + insert(iter, std::string(1, text)); + } + + void insert(LineIterator iter, strConstIter beginString, strConstIter endString) { + Line line(std::string(beginString, endString)); + insert(iter, line); + } + + void insert(LineIterator iter, const Line &line) { + insert(iter, line.begin(), line.end()); + } + + void insert(LineIterator iter, LineIterator beginLine, LineIterator endLine) { + if (iter == end()) + append(beginLine, endLine); + else { + m_chars.insert(iter.m_charsIter, beginLine.m_charsIter, endLine.m_charsIter); + 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; + } + } + + void erase(LineIterator begin) { + m_chars.erase(begin.m_charsIter); + m_colors.erase(begin.m_colorsIter); + m_flags.erase(begin.m_flagsIter); + m_colorized = false; + } + + void erase(LineIterator begin, uint64_t count) { + if (count == (uint64_t) -1) + count = m_chars.size() - (begin.m_charsIter - m_chars.begin()); + m_chars.erase(begin.m_charsIter, begin.m_charsIter + count); + m_colors.erase(begin.m_colorsIter, begin.m_colorsIter + count); + m_flags.erase(begin.m_flagsIter, begin.m_flagsIter + count); + m_colorized = false; + } + + void erase(uint64_t start, uint64_t length = (uint64_t) -1) { + if (length == (uint64_t) -1 || start + length >= m_chars.size()) + length = m_chars.size() - start; + uint64_t utf8Start = 0; for (uint64_t utf8Index = 0; utf8Index < start; ++utf8Index) { utf8Start += utf8CharLength(m_chars[utf8Start]); } @@ -418,614 +627,423 @@ public: for (uint64_t utf8Index = 0; utf8Index < length; ++utf8Index) { utf8Length += utf8CharLength(m_chars[utf8Start + utf8Length]); } - return m_chars.substr(utf8Start, utf8Length); + utf8Length = std::min(utf8Length, (uint64_t) (m_chars.size() - utf8Start)); + erase(begin() + utf8Start, utf8Length); } - return ""; - } - char operator[](uint64_t index) const { - index = std::clamp(index, (uint64_t)0, (uint64_t)(m_chars.size() - 1)); - return m_chars[index]; - } - // C++ can't overload functions based on return type, so use any type other - // than uint64_t to avoid ambiguity. - template - std::string operator[](T column) const { - uint64_t utf8Length = getStringCharacterCount(m_chars); - uint64_t index = static_cast(column); - index = std::clamp(index,(uint64_t)0,utf8Length-1); - uint64_t utf8Start = 0; - for (uint64_t utf8Index = 0; utf8Index < index; ++utf8Index) { - utf8Start += utf8CharLength(m_chars[utf8Start]); - } - uint64_t utf8CharLen = utf8CharLength(m_chars[utf8Start]); - if (utf8Start + utf8CharLen > m_chars.size()) - utf8CharLen = m_chars.size() - utf8Start; - return m_chars.substr(utf8Start, utf8CharLen); - } - - void setNeedsUpdate(bool needsUpdate) { - m_colorized = m_colorized && !needsUpdate; - } - - void append(const char *text) { - append(std::string(text)); - } - - void append(const char text) { - append(std::string(1, text)); - } - - void append(const std::string &text) { - Line line(text); - append(line); - } - - void append(const Line &line) { - append(line.begin(), line.end()); - } - - void append(LineIterator begin, LineIterator end) { - if (begin.m_charsIter < end.m_charsIter) - m_chars.append(begin.m_charsIter, end.m_charsIter); - if (begin.m_colorsIter < end.m_colorsIter) - m_colors.append(begin.m_colorsIter, end.m_colorsIter); - if (begin.m_flagsIter < end.m_flagsIter) - m_flags.append(begin.m_flagsIter, end.m_flagsIter); - m_colorized = false; - } - - void insert(LineIterator iter, const std::string &text) { - insert(iter, text.begin(), text.end()); - } - - void insert(LineIterator iter, const char text) { - insert(iter,std::string(1, text)); - } - - void insert(LineIterator iter, strConstIter beginString, strConstIter endString) { - Line line(std::string(beginString, endString)); - insert(iter, line); - } - - void insert(LineIterator iter,const Line &line) { - insert(iter, line.begin(), line.end()); - } - - void insert(LineIterator iter,LineIterator beginLine, LineIterator endLine) { - if (iter == end()) - append(beginLine, endLine); - else { - m_chars.insert(iter.m_charsIter, beginLine.m_charsIter, endLine.m_charsIter); - m_colors.insert(iter.m_colorsIter, beginLine.m_colorsIter, endLine.m_colorsIter); - m_flags.insert(iter.m_flagsIter, beginLine.m_flagsIter, endLine.m_flagsIter); + void clear() { + m_chars.clear(); + m_colors.clear(); + m_flags.clear(); m_colorized = false; } - } - void erase(LineIterator begin) { - m_chars.erase(begin.m_charsIter); - m_colors.erase(begin.m_colorsIter); - m_flags.erase(begin.m_flagsIter); - m_colorized = false; - } - - void erase(LineIterator begin, uint64_t count) { - if (count == (uint64_t) -1) - count = m_chars.size() - (begin.m_charsIter - m_chars.begin()); - m_chars.erase(begin.m_charsIter, begin.m_charsIter + count); - m_colors.erase(begin.m_colorsIter, begin.m_colorsIter + count); - m_flags.erase(begin.m_flagsIter, begin.m_flagsIter + count); - m_colorized = false; - } - - void erase(uint64_t start, uint64_t length = (uint64_t)-1) { - if (length == (uint64_t)-1 || start + length >= m_chars.size()) - length = m_chars.size() - start; - uint64_t utf8Start= 0; - for (uint64_t utf8Index = 0; utf8Index < start; ++utf8Index) { - utf8Start += utf8CharLength(m_chars[utf8Start]); + void 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; } - uint64_t utf8Length = 0; - for (uint64_t utf8Index = 0; utf8Index < length; ++utf8Index) { - utf8Length += utf8CharLength(m_chars[utf8Start + utf8Length]); + + void setLine(const Line &text) { + m_chars = text.m_chars; + m_colors = text.m_colors; + m_flags = text.m_flags; + m_colorized = text.m_colorized; } - erase(begin() + utf8Start, utf8Length); - } - void clear() { - m_chars.clear(); - m_colors.clear(); - m_flags.clear(); - m_colorized = false; - } - - void 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; - } - - void setLine(const Line &text) { - m_chars = text.m_chars; - m_colors = text.m_colors; - m_flags = text.m_flags; - m_colorized = text.m_colorized; - } - - - bool needsUpdate() const { - return !m_colorized; - } - - }; - - using Lines = std::vector; - - 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); - - std::string m_name; - Keywords m_keywords; - Identifiers m_identifiers; - Identifiers m_preprocIdentifiers; - std::string m_singleLineComment, m_commentEnd, m_commentStart, m_globalDocComment, m_docComment, m_blockDocComment; - char m_preprocChar; - bool m_autoIndentation; - - TokenizeCallback m_tokenize; - - 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) {} - - static const LanguageDefinition& CPlusPlus(); - static const LanguageDefinition& HLSL(); - static const LanguageDefinition& GLSL(); - static const LanguageDefinition& C(); - static const LanguageDefinition& SQL(); - static const LanguageDefinition& AngelScript(); - static const LanguageDefinition& Lua(); - }; - void clearErrorMarkers() { - m_errorMarkers.clear(); - m_errorHoverBoxes.clear(); - } - void clearGotoBoxes() { m_errorGotoBoxes.clear(); } - void clearCursorBoxes() { m_cursorBoxes.clear(); } - - void clearActionables() { - clearErrorMarkers(); - clearGotoBoxes(); - clearCursorBoxes(); - } - - struct Selection { - Coordinates m_start; - Coordinates 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); + bool needsUpdate() const { + return !m_colorized; } + + }; + + using Lines = std::vector; + + 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); + + std::string m_name; + Keywords m_keywords; + Identifiers m_identifiers; + Identifiers m_preprocIdentifiers; + std::string m_singleLineComment, m_commentEnd, m_commentStart, m_globalDocComment, m_docComment, m_blockDocComment; + char m_preprocChar; + bool m_autoIndentation; + TokenizeCallback m_tokenize; + 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) {} + + static const LanguageDefinition &CPlusPlus(); + static const LanguageDefinition &HLSL(); + static const LanguageDefinition &GLSL(); + static const LanguageDefinition &C(); + static const LanguageDefinition &SQL(); + static const LanguageDefinition &AngelScript(); + static const LanguageDefinition &Lua(); + }; + TextEditor(); + ~TextEditor(); + + private: + class UndoRecord { + public: + UndoRecord() {} + + ~UndoRecord() {} + + UndoRecord( const std::string &added, + const TextEditor::Selection addedSelection, + const std::string &removed, + const TextEditor::Selection removedSelection, + TextEditor::EditorState &before, + TextEditor::EditorState &after); + + void undo(TextEditor *editor); + void redo(TextEditor *editor); + + std::string m_added; + Selection m_addedSelection; + std::string m_removed; + Selection m_removedSelection; + EditorState m_before; + EditorState m_after; + }; + + typedef std::vector UndoBuffer; + + struct MatchedBracket { + bool m_active = false; + bool m_changed = false; + Coordinates m_nearCursor = {}; + Coordinates m_matched = {}; + static const std::string s_separators; + static const std::string s_operators; + + MatchedBracket(const MatchedBracket &other) : m_active(other.m_active), m_changed(other.m_changed), + m_nearCursor(other.m_nearCursor), + m_matched(other.m_matched) {} + + MatchedBracket() : m_active(false), m_changed(false), m_nearCursor(0, 0), m_matched(0, 0) {} + MatchedBracket(bool active, bool changed, const Coordinates &nearCursor, const Coordinates &matched) + : m_active(active), m_changed(changed), m_nearCursor(nearCursor), m_matched(matched) {} + + bool checkPosition(TextEditor *editor, const Coordinates &from); + bool isNearABracket(TextEditor *editor, const Coordinates &from); + int32_t detectDirection(TextEditor *editor, const Coordinates &from); + void findMatchingBracket(TextEditor *editor); + bool isActive() const { return m_active; } + bool hasChanged() const { return m_changed; } + }; + + + public: +// Rendering + inline void setTextChanged(bool value) { m_textChanged = value; } + inline bool isTextChanged() { return m_textChanged; } + inline void clearBreakpointsChanged() { m_breakPointsChanged = false; } + inline bool isBreakpointsChanged() { return m_breakPointsChanged; } + inline void setShowCursor(bool value) { m_showCursor = value; } + inline void setReadOnly(bool value) { m_readOnly = value; } + inline void setShowLineNumbers(bool value) { m_showLineNumbers = value; } + inline void setImGuiChildIgnored(bool value) { m_ignoreImGuiChild = value; } + inline bool isImGuiChildIgnored() const { return m_ignoreImGuiChild; } + inline void setShowWhitespaces(bool value) { m_showWhitespaces = value; } + inline bool isShowingWhitespaces() const { return m_showWhitespaces; } + void setTabSize(int32_t value); + inline int32_t getTabSize() const { return m_tabSize; } + float getPageSize() const; + ImVec2 &getCharAdvance() { return m_charAdvance; } + static const Palette &getDarkPalette(); + static const Palette &getLightPalette(); + static const Palette &getRetroBluePalette(); + bool isEndOfLine(const Coordinates &coordinates) const; + bool isEndOfFile(const Coordinates &coordinates) const; + Coordinates getCursorPosition() const { return setCoordinates(m_state.m_cursorPosition); } + void setCursorPosition(const Coordinates &position); + bool raiseContextMenu() { return m_raiseContextMenu; } + void clearRaiseContextMenu() { m_raiseContextMenu = false; } + inline void setHandleMouseInputs(bool value) { m_handleMouseInputs = value; } + inline bool isHandleMouseInputsEnabled() const { return m_handleKeyboardInputs; } + inline void setHandleKeyboardInputs(bool value) { m_handleKeyboardInputs = value; } + inline bool isHandleKeyboardInputsEnabled() const { return m_handleKeyboardInputs; } + + void setTopMarginChanged(int32_t newMargin) { + m_newTopMargin = newMargin; + m_topMarginChanged = true; } - Coordinates getSelectedLines() { - return Coordinates(m_start.m_line,m_end.m_line); + + void setFocusAtCoords(const Coordinates &coords) { + m_focusAtCoords = coords; + m_updateFocus = true; } - Coordinates 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); + + void setTopLine(); + void render(const char *title, const ImVec2 &size = ImVec2(), bool border = false); + + void clearErrorMarkers() { + m_errorMarkers.clear(); + m_errorHoverBoxes.clear(); } - bool isSingleLine() { - return m_start.m_line == m_end.m_line; + + void clearGotoBoxes() { m_errorGotoBoxes.clear(); } + void clearCursorBoxes() { m_cursorBoxes.clear(); } + + void clearActionables() { + clearErrorMarkers(); + clearGotoBoxes(); + clearCursorBoxes(); } - }; - TextEditor(); - ~TextEditor(); + void addClickableText(std::string text) { m_clickableText.push_back(text); } + void setErrorMarkers(const ErrorMarkers &markers) { m_errorMarkers = markers; } + Breakpoints &getBreakpoints() { return m_breakpoints; } + void setBreakpoints(const Breakpoints &markers) { m_breakpoints = markers; } + ImVec2 underwaves(ImVec2 pos, uint32_t nChars, ImColor color = ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg = ImVec2(0, 0)); + void setLongestLineLength(uint64_t line) { m_longestLineLength = line; } + uint64_t getLongestLineLength() const { return m_longestLineLength; } - void setLanguageDefinition(const LanguageDefinition& aLanguageDef); - const LanguageDefinition& getLanguageDefinition() const { return m_languageDefinition; } + private: + void ensureCursorVisible(); + void resetCursorBlinkTime(); + void renderText(const char *title, const ImVec2 &lineNumbersStartPos, const ImVec2 &textEditorSize); + void setFocus(); + void preRender(); + void drawSelection(float lineNo); + void drawLineNumbers(ImVec2 position, float lineNo, const ImVec2 &contentSize, bool focused, ImDrawList *drawList); + void renderCursor(float lineNo, ImDrawList *drawList); + void renderGotoButtons(float lineNo); + void drawText(Coordinates &lineStart, uint64_t i, uint32_t tokenLength, char color); + void postRender(const char *title, ImVec2 position, float lineNo); + ImVec2 calculateCharAdvance() const; +// Highlighting + public: + bool isColorizerEnabled() const { return m_colorizerEnabled; } + void colorize(); + void setLanguageDefinition(const LanguageDefinition &aLanguageDef); + const LanguageDefinition &getLanguageDefinition() const { return m_languageDefinition; } + static const Palette &getPalette(); + static void setPalette(const Palette &value); - static const Palette& getPalette(); - static void setPalette(const Palette& value); - - void setErrorMarkers(const ErrorMarkers& markers) { m_errorMarkers = markers; } - Breakpoints &getBreakpoints() { return m_breakpoints; } - void setBreakpoints(const Breakpoints& markers) { m_breakpoints = markers; } - ImVec2 underwaves(ImVec2 pos, uint32_t nChars, ImColor color= ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg= ImVec2(0, 0)); - - void render(const char* title, const ImVec2& size = ImVec2(), bool border = false); - void setText(const std::string& text, bool undo = false); - void jumpToLine(int32_t line=-1); - void jumpToCoords(const Coordinates &coords); - void setLongestLineLength(uint64_t line) { m_longestLineLength = line; } - uint64_t getLongestLineLength() const { return m_longestLineLength; } - std::string getText() const; - bool 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; + void setNeedsUpdate(uint32_t line, bool needsUpdate) { + if (line < m_lines.size()) + m_lines[line].setNeedsUpdate(needsUpdate); } - return false; - } - void setTopLine(); - void setNeedsUpdate (uint32_t line, bool needsUpdate) { - if (line < m_lines.size()) - m_lines[line].setNeedsUpdate(needsUpdate); - } - - void setColorizedLine(uint64_t line, const std::string &tokens) { - if (line < m_lines.size()) { - auto &lineTokens = m_lines[line].m_colors; - if (lineTokens.size() != tokens.size()) { - lineTokens.resize(tokens.size()); - std::fill(lineTokens.begin(), lineTokens.end(), 0x00); - } - bool needsUpdate = false; - for (uint64_t i = 0; i < tokens.size(); ++i) { - if (tokens[i] != 0x00) { - if (tokens[i] != lineTokens[i]) { - lineTokens[i] = tokens[i]; - needsUpdate = true; + void setColorizedLine(uint64_t line, const std::string &tokens) { + if (line < m_lines.size()) { + auto &lineTokens = m_lines[line].m_colors; + if (lineTokens.size() != tokens.size()) { + lineTokens.resize(tokens.size()); + std::fill(lineTokens.begin(), lineTokens.end(), 0x00); + } + bool needsUpdate = false; + for (uint64_t i = 0; i < tokens.size(); ++i) { + if (tokens[i] != 0x00) { + if (tokens[i] != lineTokens[i]) { + lineTokens[i] = tokens[i]; + needsUpdate = true; + } } } + setNeedsUpdate(line, needsUpdate); } - setNeedsUpdate(line, needsUpdate); - } - } - - void setScrollY(); - std::vector getTextLines() const; - - std::string getSelectedText() const; - - std::string getLineText(int32_t line)const; - void setSourceCodeEditor(TextEditor *editor) { m_sourceCodeEditor = editor; } - TextEditor *GetSourceCodeEditor() { - if(m_sourceCodeEditor != nullptr) - return m_sourceCodeEditor; - return this; - } - - class FindReplaceHandler; - -public: - void addClickableText(std::string text) { - m_clickableText.push_back(text); - } - FindReplaceHandler *getFindReplaceHandler() { return &m_findReplaceHandler; } - int32_t getTotalLines() const { return (int32_t)m_lines.size(); } - bool isOverwrite() const { return m_overwrite; } - void setTopMarginChanged(int32_t newMargin) { - m_newTopMargin = newMargin; - m_topMarginChanged = true; - } - void setFocusAtCoords(const Coordinates &coords) { - m_focusAtCoords = coords; - m_updateFocus = true; - } - void setOverwrite(bool value) { m_overwrite = value; } - - std::string replaceStrings(std::string string, const std::string &search, const std::string &replace); - static std::vector splitString(const std::string &string, const std::string &delimiter, bool removeEmpty); - std::string replaceTabsWithSpaces(const std::string& string, uint32_t tabSize); - std::string preprocessText(const std::string &code); - - void setReadOnly(bool value); - bool isEndOfLine(const Coordinates &coordinates) const; - bool isEndOfFile(const Coordinates &coordinates) const; - bool isReadOnly() const { return m_readOnly; } - bool isTextChanged() const { return m_textChanged; } - void setTextChanged(bool value) { m_textChanged = value; } - bool isBreakpointsChanged() const { return m_breakPointsChanged; } - void clearBreakpointsChanged() { m_breakPointsChanged = false; } - - void setShowCursor(bool value) { m_showCursor = value; } - void setShowLineNumbers(bool value) { m_showLineNumbers = value; } - - bool isColorizerEnabled() const { return m_colorizerEnabled; } - void colorize(); - - Coordinates getCursorPosition() const { return setCoordinates(m_state.m_cursorPosition); } - void setCursorPosition(const Coordinates& position); - - bool raiseContextMenu() { return m_raiseContextMenu; } - void clearRaiseContextMenu() { m_raiseContextMenu = false; } - - inline void setHandleMouseInputs (bool value){ m_handleMouseInputs = value;} - inline bool isHandleMouseInputsEnabled() const { return m_handleKeyboardInputs; } - - inline void setHandleKeyboardInputs (bool value){ m_handleKeyboardInputs = value;} - inline bool isHandleKeyboardInputsEnabled() const { return m_handleKeyboardInputs; } - - inline void setImGuiChildIgnored (bool value){ m_ignoreImGuiChild = value;} - inline bool isImGuiChildIgnored() const { return m_ignoreImGuiChild; } - - inline void setShowWhitespaces(bool value) { m_showWhitespaces = value; } - inline bool isShowingWhitespaces() const { return m_showWhitespaces; } - - void setTabSize(int32_t value); - inline int32_t getTabSize() const { return m_tabSize; } - - void insertText(const std::string& value); - void insertText(const char* value); - void appendLine(const std::string &value); - - void moveUp(int32_t amount = 1, bool select = false); - void moveDown(int32_t amount = 1, bool select = false); - void moveLeft(int32_t amount = 1, bool select = false, bool wordMode = false); - void moveRight(int32_t amount = 1, bool select = false, bool wordMode = false); - void moveTop(bool select = false); - void moveBottom(bool select = false); - void moveHome(bool select = false); - void moveEnd(bool select = false); - void moveToMatchedBracket(bool select = false); - - void setSelection(const Selection& selection); - Selection getSelection() const; - void selectWordUnderCursor(); - void selectAll(); - bool hasSelection() const; - - void copy(); - void cut(); - void paste(); - void deleteChar(); - float getPageSize() const; - - ImVec2 &getCharAdvance() { return m_charAdvance; } - - bool canUndo(); - bool canRedo() const; - void undo(int32_t steps = 1); - void redo(int32_t steps = 1); - - void deleteWordLeft(); - void deleteWordRight(); - void backspace(); - - static const Palette& getDarkPalette(); - static const Palette& getLightPalette(); - static const Palette& getRetroBluePalette(); - -private: - typedef std::vector> RegexList; - - struct EditorState - { - Selection m_selection; - Coordinates m_cursorPosition; - }; - - -public: - class FindReplaceHandler { - public: - FindReplaceHandler(); - typedef std::vector Matches; - Matches &getMatches() { return m_matches; } - bool findNext(TextEditor *editor); - uint32_t findMatch(TextEditor *editor, bool isNex); - bool replace(TextEditor *editor, bool right); - bool replaceAll(TextEditor *editor); - std::string &getFindWord() { return m_findWord; } - void setFindWord(TextEditor *editor, const std::string &findWord) { - if (findWord != m_findWord) { - findAllMatches(editor, findWord); - m_findWord = findWord; - } - } - std::string &getReplaceWord() { return m_replaceWord; } - void setReplaceWord(const std::string &replaceWord) { m_replaceWord = replaceWord; } - void selectFound(TextEditor *editor, int32_t found); - void findAllMatches(TextEditor *editor, std::string findWord); - uint32_t findPosition(TextEditor *editor, Coordinates pos, bool isNext); - bool getMatchCase() const { return m_matchCase; } - void setMatchCase(TextEditor *editor, bool matchCase) { - if (matchCase != m_matchCase) { - m_matchCase = matchCase; - m_optionsChanged = true; - findAllMatches(editor, m_findWord); - } - } - bool getWholeWord() const { return m_wholeWord; } - void setWholeWord(TextEditor *editor, bool wholeWord) { - if (wholeWord != m_wholeWord) { - m_wholeWord = wholeWord; - m_optionsChanged = true; - findAllMatches(editor, m_findWord); - } - } - bool getFindRegEx() const { return m_findRegEx; } - void setFindRegEx(TextEditor *editor, bool findRegEx) { - if (findRegEx != m_findRegEx) { - m_findRegEx = findRegEx; - m_optionsChanged = true; - findAllMatches(editor, m_findWord); - } - } - void resetMatches() { - m_matches.clear(); - m_findWord = ""; } private: - std::string m_findWord; - std::string m_replaceWord; - bool m_matchCase; - bool m_wholeWord; - bool m_findRegEx; - bool m_optionsChanged; - Matches m_matches; - }; - FindReplaceHandler m_findReplaceHandler; -private: - class UndoRecord - { - public: - UndoRecord() {} - ~UndoRecord() {} + void colorizeRange(); + void colorizeInternal(); +//Editing + public: + void deleteWordLeft(); + void deleteWordRight(); + void backspace(); + bool canUndo(); + bool canRedo() const; + void undo(int32_t steps = 1); + void redo(int32_t steps = 1); + void copy(); + void cut(); + void paste(); + void deleteChar(); + void insertText(const std::string &value); + void insertText(const char *value); + void appendLine(const std::string &value); + void setOverwrite(bool value) { m_overwrite = value; } + int32_t getTotalLines() const { return (int32_t) m_lines.size(); } + bool isOverwrite() const { return m_overwrite; } + void setText(const std::string &text, bool undo = false); + std::string getText() const; + void setScrollY(); + std::vector getTextLines() const; + std::string getSelectedText() const; + std::string getLineText(int32_t line) const; - UndoRecord( - const std::string& added, - const TextEditor::Selection addedSelection, + private: + float textDistanceToLineStart(const Coordinates &from) const; + std::string getText(const Selection &selection) const; + Coordinates setCoordinates(const Coordinates &value) const; + Coordinates setCoordinates(int32_t line, int32_t column) const; + Selection setCoordinates(const Selection &value) const; + void advance(Coordinates &coordinates) const; + void deleteRange(const Selection &selection); + int32_t insertTextAt(Coordinates &where, const std::string &value); + void removeLine(int32_t start, int32_t end); + void removeLine(int32_t index); + Line &insertLine(int32_t index); + void insertLine(int32_t index, const std::string &text); + void enterCharacter(ImWchar character, bool shift); + void deleteSelection(); +// Navigating + public: + void jumpToLine(int32_t line = -1); + void jumpToCoords(const Coordinates &coords); + void moveUp(int32_t amount = 1, bool select = false); + void moveDown(int32_t amount = 1, bool select = false); + void moveLeft(int32_t amount = 1, bool select = false, bool wordMode = false); + void moveRight(int32_t amount = 1, bool select = false, bool wordMode = false); + void moveTop(bool select = false); + void moveBottom(bool select = false); + void moveHome(bool select = false); + void moveEnd(bool select = false); + void moveToMatchedBracket(bool select = false); + private: + Coordinates findWordStart(const Coordinates &from) const; + Coordinates findWordEnd(const Coordinates &from) const; + Coordinates findPreviousWord(const Coordinates &from) const; + Coordinates findNextWord(const Coordinates &from) const; + uint32_t skipSpaces(const Coordinates &from); +//Support + public: + std::string replaceStrings(std::string string, const std::string &search, const std::string &replace); + static std::vector + splitString(const std::string &string, const std::string &delimiter, bool removeEmpty); + std::string replaceTabsWithSpaces(const std::string &string, uint32_t tabSize); + std::string preprocessText(const std::string &code); + FindReplaceHandler *getFindReplaceHandler() { return &m_findReplaceHandler; } + void setSourceCodeEditor(TextEditor *editor) { m_sourceCodeEditor = editor; } - const std::string& removed, - const TextEditor::Selection removedSelection, + TextEditor *GetSourceCodeEditor() { + if (m_sourceCodeEditor != nullptr) + return m_sourceCodeEditor; + return this; + } - TextEditor::EditorState& before, - TextEditor::EditorState& after); + void setSelection(const Selection &selection); + Selection getSelection() const; + void selectWordUnderCursor(); + void selectAll(); + bool hasSelection() const; - void undo(TextEditor* editor); - void redo(TextEditor* editor); + bool 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; + } + return false; + } - std::string m_added; - Selection m_addedSelection; + private: + void addUndo(UndoRecord &value); + TextEditor::PaletteIndex getColorIndexFromFlags(Line::Flags flags); + void handleKeyboardInputs(); + void handleMouseInputs(); +// utf8 - std::string m_removed; - Selection m_removedSelection; + private: + Coordinates screenPosToCoordinates(const ImVec2 &position) const; + int32_t lineCoordinateToIndex(const Coordinates &coordinates) const; + Coordinates getCharacterCoordinates(int32_t line, int32_t index) const; + int32_t getLineCharacterCount(int32_t line) const; + uint64_t getLineByteCount(int32_t line) const; + int32_t getLineMaxColumn(int32_t line) const; - EditorState m_before; - EditorState m_after; - }; + public: + FindReplaceHandler m_findReplaceHandler; + private: + float m_lineSpacing = 1.0F; + Lines m_lines; + EditorState m_state = {}; + UndoBuffer m_undoBuffer; + int32_t m_undoIndex = 0; + bool m_scrollToBottom = false; + float m_topMargin = 0.0F; + float m_newTopMargin = 0.0F; + float m_oldTopMargin = 0.0F; + bool m_topMarginChanged = false; - typedef std::vector UndoBuffer; + int32_t m_tabSize = 4; + bool m_overwrite = false; + bool m_readOnly = false; + bool m_withinRender = false; + bool m_scrollToCursor = false; + bool m_scrollToTop = false; + bool m_textChanged = false; + bool m_colorizerEnabled = true; + float m_lineNumberFieldWidth = 0.0F; + uint64_t m_longestLineLength = 0; + float m_leftMargin = 10.0; + float m_topLine = 0.0F; + bool m_setTopLine = false; + bool m_breakPointsChanged = false; + bool m_handleKeyboardInputs = true; + bool m_handleMouseInputs = true; + bool m_ignoreImGuiChild = false; + bool m_showWhitespaces = true; - struct MatchedBracket { - bool m_active=false; - bool m_changed=false; - Coordinates m_nearCursor = {}; - Coordinates m_matched = {}; - static const std::string s_separators; - static const std::string s_operators; - MatchedBracket(const MatchedBracket &other) : m_active(other.m_active), m_changed(other.m_changed), m_nearCursor(other.m_nearCursor), m_matched(other.m_matched) {} - MatchedBracket() : m_active(false), m_changed(false), m_nearCursor(0, 0), m_matched(0, 0) {} - MatchedBracket(bool active, bool changed, const Coordinates &nearCursor, const Coordinates &matched) : m_active(active), m_changed(changed), m_nearCursor(nearCursor), m_matched(matched) {} - bool checkPosition(TextEditor *editor, const Coordinates &from); - bool isNearABracket(TextEditor *editor, const Coordinates &from); - int32_t detectDirection(TextEditor *editor, const Coordinates &from); + MatchedBracket m_matchedBracket = {}; + Palette m_palette = {}; + LanguageDefinition m_languageDefinition = {}; + RegexList m_regexList; + bool m_updateFlags = true; + Breakpoints m_breakpoints = {}; + ErrorMarkers m_errorMarkers = {}; + ErrorHoverBoxes m_errorHoverBoxes = {}; + ErrorGotoBoxes m_errorGotoBoxes = {}; + CursorBoxes m_cursorBoxes = {}; + ImVec2 m_charAdvance = {}; + Selection m_interactiveSelection = {}; + uint64_t m_startTime = 0; + std::vector m_defines; + TextEditor *m_sourceCodeEditor = nullptr; + float m_shiftedScrollY = 0; + float m_scrollYIncrement = 0.0F; + bool m_setScrollY = false; + float m_numberOfLinesDisplayed = 0; + float m_lastClick = -1.0F; + bool m_showCursor = true; + bool m_showLineNumbers = true; + bool m_raiseContextMenu = false; + Coordinates m_focusAtCoords = {}; + bool m_updateFocus = false; - void findMatchingBracket(TextEditor *editor); - bool isActive() const { return m_active; } - bool hasChanged() const { return m_changed; } + std::vector m_clickableText; + + static const int32_t s_cursorBlinkInterval; + static const int32_t s_cursorBlinkOnTime; + static ImVec2 s_cursorScreenPosition; }; - void colorizeRange(); - void colorizeInternal(); - float textDistanceToLineStart(const Coordinates& from) const; - void ensureCursorVisible(); - std::string getText(const Selection& selection) const; - Coordinates setCoordinates(const Coordinates& value) const; - Coordinates setCoordinates( int32_t line, int32_t column ) const; - Selection setCoordinates(const Selection &value) const; - void advance(Coordinates& coordinates) const; - void deleteRange(const Selection& selection); - int32_t insertTextAt(Coordinates &where, const std::string &value); - void addUndo(UndoRecord& value); - Coordinates screenPosToCoordinates(const ImVec2& position) const; - Coordinates findWordStart(const Coordinates& from) const; - Coordinates findWordEnd(const Coordinates& from) const; - Coordinates findPreviousWord(const Coordinates& from) const; - Coordinates findNextWord(const Coordinates& from) const; - int32_t lineCoordinateToIndex(const Coordinates& coordinates) const; - Coordinates getCharacterCoordinates(int32_t line, int32_t index) const; - int32_t getLineCharacterCount(int32_t line) const; - uint64_t getLineByteCount(int32_t line) const; - int32_t getLineMaxColumn(int32_t line) const; - void removeLine(int32_t start, int32_t end); - void removeLine(int32_t index); - Line& insertLine(int32_t index); - void insertLine(int32_t index, const std::string &text); - void enterCharacter(ImWchar character, bool shift); - void deleteSelection(); - TextEditor::PaletteIndex getColorIndexFromFlags(Line::Flags flags); - void resetCursorBlinkTime(); - uint32_t skipSpaces(const Coordinates &from); - void handleKeyboardInputs(); - void handleMouseInputs(); - void renderText(const char *title, const ImVec2 &lineNumbersStartPos, const ImVec2 &textEditorSize); - void setFocus(); - float m_lineSpacing = 1.0F; - Lines m_lines; - EditorState m_state = {}; - UndoBuffer m_undoBuffer; - int32_t m_undoIndex = 0; - bool m_scrollToBottom = false; - float m_topMargin = 0.0F; - float m_newTopMargin = 0.0F; - float m_oldTopMargin = 0.0F; - bool m_topMarginChanged = false; + bool tokenizeCStyleString(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end); + bool tokenizeCStyleCharacterLiteral(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end); + bool tokenizeCStyleIdentifier(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end); + bool tokenizeCStyleNumber(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end); + bool tokenizeCStyleOperator(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end); + bool tokenizeCStyleSeparator(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end); - int32_t m_tabSize = 4; - bool m_overwrite = false; - bool m_readOnly = false; - bool m_withinRender = false; - bool m_scrollToCursor = false; - bool m_scrollToTop = false; - bool m_textChanged = false; - bool m_colorizerEnabled = true; - float m_lineNumberFieldWidth = 0.0F; - uint64_t m_longestLineLength = 0; - float m_leftMargin = 10.0; - float m_topLine = 0.0F; - bool m_setTopLine = false; - bool m_breakPointsChanged = false; - bool m_handleKeyboardInputs = true; - bool m_handleMouseInputs = true; - bool m_ignoreImGuiChild = false; - bool m_showWhitespaces = true; - - MatchedBracket m_matchedBracket={}; - Palette m_palette = {}; - LanguageDefinition m_languageDefinition = {}; - RegexList m_regexList; - bool m_updateFlags = true; - Breakpoints m_breakpoints = {}; - ErrorMarkers m_errorMarkers = {}; - ErrorHoverBoxes m_errorHoverBoxes = {}; - ErrorGotoBoxes m_errorGotoBoxes = {}; - CursorBoxes m_cursorBoxes = {}; - ImVec2 m_charAdvance = {}; - Selection m_interactiveSelection = {}; - std::string m_lineBuffer; - uint64_t m_startTime = 0; - std::vector m_defines; - TextEditor *m_sourceCodeEditor = nullptr; - float m_shiftedScrollY = 0; - float m_scrollYIncrement = 0.0F; - bool m_setScrollY = false; - float m_numberOfLinesDisplayed = 0; - float m_lastClick = -1.0F; - bool m_showCursor = true; - bool m_showLineNumbers = true; - bool m_raiseContextMenu = false; - Coordinates m_focusAtCoords = {}; - bool m_updateFocus = false; - - std::vector m_clickableText; - - static const int32_t s_cursorBlinkInterval; - static const int32_t s_cursorBlinkOnTime; -}; - -bool tokenizeCStyleString(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end); -bool tokenizeCStyleCharacterLiteral(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end); -bool tokenizeCStyleIdentifier(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end); -bool tokenizeCStyleNumber(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end); -bool tokenizeCStyleOperator(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end); -bool tokenizeCStyleSeparator(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end); \ No newline at end of file +} \ No newline at end of file diff --git a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp index 8487d4e52..bf37e7908 100644 --- a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp +++ b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp @@ -11,1767 +11,1836 @@ #include "imgui.h" // for imGui::GetCurrentWindow() #include "imgui_internal.h" - -template -bool equals(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p) { - for (; first1 != last1 && first2 != last2; ++first1, ++first2) { - if (!p(*first1, *first2)) - return false; +namespace hex::ui { + template + bool equals(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p) { + for (; first1 != last1 && first2 != last2; ++first1, ++first2) { + if (!p(*first1, *first2)) + return false; + } + return first1 == last1 && first2 == last2; } - return first1 == last1 && first2 == last2; -} -const int32_t TextEditor::s_cursorBlinkInterval = 1200; -const int32_t TextEditor::s_cursorBlinkOnTime = 800; + const int32_t TextEditor::s_cursorBlinkInterval = 1200; + const int32_t TextEditor::s_cursorBlinkOnTime = 800; + ImVec2 TextEditor::s_cursorScreenPosition; -TextEditor::Palette s_paletteBase = TextEditor::getDarkPalette(); + TextEditor::Palette s_paletteBase = TextEditor::getDarkPalette(); -TextEditor::FindReplaceHandler::FindReplaceHandler() : m_wholeWord(false), m_findRegEx(false), m_matchCase(false) {} -const std::string TextEditor::MatchedBracket::s_separators = "()[]{}"; -const std::string TextEditor::MatchedBracket::s_operators = "<>"; + TextEditor::FindReplaceHandler::FindReplaceHandler() : m_wholeWord(false), m_findRegEx(false), m_matchCase(false) {} + const std::string TextEditor::MatchedBracket::s_separators = "()[]{}"; + const std::string TextEditor::MatchedBracket::s_operators = "<>"; // https://en.wikipedia.org/wiki/UTF-8 // We assume that the char is a standalone character (<128) or a leading byte of an UTF-8 code sequence (non-10xxxxxx code) -int32_t utf8CharLength(uint8_t c) { - if ((c & 0xFE) == 0xFC) - return 6; - if ((c & 0xFC) == 0xF8) - return 5; - if ((c & 0xF8) == 0xF0) - return 4; - if ((c & 0xF0) == 0xE0) - return 3; - if ((c & 0xE0) == 0xC0) - return 2; - return 1; -} - -int32_t getStringCharacterCount(const std::string& str) { - if (str.empty()) - return 0; - int32_t count = 0; - for (uint32_t idx = 0; idx < str.size(); count++) - idx += utf8CharLength(str[idx]); - return count; -} - - -TextEditor::TextEditor() { - m_startTime = ImGui::GetTime() * 1000; - setLanguageDefinition(LanguageDefinition::HLSL()); - m_lines.push_back(Line()); -} - -TextEditor::~TextEditor() { -} - - -ImVec2 TextEditor::underwaves(ImVec2 pos , uint32_t nChars, ImColor color, const ImVec2 &size_arg) { - auto save = ImGui::GetStyle().AntiAliasedLines; - ImGui::GetStyle().AntiAliasedLines = false; - ImGuiWindow *window = ImGui::GetCurrentWindow(); - window->DC.CursorPos =pos; - const ImVec2 label_size = ImGui::CalcTextSize("W", nullptr, true); - ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x, label_size.y); - float lineWidth = size.x / 3.0f + 0.5f; - float halfLineW = lineWidth / 2.0f; - - for (uint32_t i = 0; i < nChars; i++) { - pos = window->DC.CursorPos; - float lineY = pos.y + size.y; - - ImVec2 pos1_1 = ImVec2(pos.x + 0*lineWidth, lineY + halfLineW); - ImVec2 pos1_2 = ImVec2(pos.x + 1*lineWidth, lineY - halfLineW); - ImVec2 pos2_1 = ImVec2(pos.x + 2*lineWidth, lineY + halfLineW); - ImVec2 pos2_2 = ImVec2(pos.x + 3*lineWidth, lineY - halfLineW); - - ImGui::GetWindowDrawList()->AddLine(pos1_1, pos1_2, ImU32(color), 0.4f); - ImGui::GetWindowDrawList()->AddLine(pos1_2, pos2_1, ImU32(color), 0.4f); - ImGui::GetWindowDrawList()->AddLine(pos2_1, pos2_2, ImU32(color), 0.4f); - - window->DC.CursorPos = ImVec2(pos.x + size.x, pos.y); + int32_t utf8CharLength(uint8_t c) { + if ((c & 0xFE) == 0xFC) + return 6; + if ((c & 0xFC) == 0xF8) + return 5; + if ((c & 0xF8) == 0xF0) + return 4; + if ((c & 0xF0) == 0xE0) + return 3; + if ((c & 0xE0) == 0xC0) + return 2; + return 1; } - auto ret = window->DC.CursorPos; - ret.y += size.y; - return ret; -} -void TextEditor::setLanguageDefinition(const LanguageDefinition &languageDef) { - m_languageDefinition = languageDef; - m_regexList.clear(); + int32_t getStringCharacterCount(const std::string &str) { + if (str.empty()) + return 0; + int32_t count = 0; + for (uint32_t idx = 0; idx < str.size(); count++) + idx += utf8CharLength(str[idx]); + return count; + } - for (auto &r : m_languageDefinition.m_tokenRegexStrings) - m_regexList.push_back(std::make_pair(std::regex(r.first, std::regex_constants::optimize), r.second)); - colorize(); -} + TextEditor::TextEditor() { + m_startTime = ImGui::GetTime() * 1000; + setLanguageDefinition(LanguageDefinition::HLSL()); + m_lines.push_back(Line()); + } -const TextEditor::Palette& TextEditor::getPalette() { return s_paletteBase; } + TextEditor::~TextEditor() { + } -void TextEditor::setPalette(const Palette &value) { - s_paletteBase = value; -} -bool TextEditor::isEndOfLine(const Coordinates &coordinates) const { - if (coordinates.m_line < (int32_t)m_lines.size()) - return coordinates.m_column >= getStringCharacterCount(m_lines[coordinates.m_line].m_chars); - return true; -} + ImVec2 TextEditor::underwaves(ImVec2 pos, uint32_t nChars, ImColor color, const ImVec2 &size_arg) { + auto save = ImGui::GetStyle().AntiAliasedLines; + ImGui::GetStyle().AntiAliasedLines = false; + ImGuiWindow *window = ImGui::GetCurrentWindow(); + window->DC.CursorPos = pos; + const ImVec2 label_size = ImGui::CalcTextSize("W", nullptr, true); + ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x, label_size.y); + float lineWidth = size.x / 3.0f + 0.5f; + float halfLineW = lineWidth / 2.0f; -bool TextEditor::isEndOfFile(const Coordinates &coordinates) const { - if (coordinates.m_line < (int32_t)m_lines.size()) - return coordinates.m_line >= (int32_t)m_lines.size() - 1 && isEndOfLine(coordinates); - return true; -} + for (uint32_t i = 0; i < nChars; i++) { + pos = window->DC.CursorPos; + float lineY = pos.y + size.y; -std::string TextEditor::getText(const Selection &from) const { - std::string result = ""; - auto selection = setCoordinates(from); - auto columns = selection.getSelectedColumns(); - if (selection.isSingleLine()) - result = m_lines[selection.m_start.m_line].substr(columns.m_line, columns.m_column,Line::LinePart::Utf8); - else { - auto lines = selection.getSelectedLines(); - result = m_lines[lines.m_line].substr(columns.m_line,-1,Line::LinePart::Utf8) + '\n'; - for (uint64_t i = lines.m_line+1; i < lines.m_column; i++) { - result += m_lines[i].m_chars + '\n'; + ImVec2 pos1_1 = ImVec2(pos.x + 0 * lineWidth, lineY + halfLineW); + ImVec2 pos1_2 = ImVec2(pos.x + 1 * lineWidth, lineY - halfLineW); + ImVec2 pos2_1 = ImVec2(pos.x + 2 * lineWidth, lineY + halfLineW); + ImVec2 pos2_2 = ImVec2(pos.x + 3 * lineWidth, lineY - halfLineW); + + ImGui::GetWindowDrawList()->AddLine(pos1_1, pos1_2, ImU32(color), 0.4f); + ImGui::GetWindowDrawList()->AddLine(pos1_2, pos2_1, ImU32(color), 0.4f); + ImGui::GetWindowDrawList()->AddLine(pos2_1, pos2_2, ImU32(color), 0.4f); + + window->DC.CursorPos = ImVec2(pos.x + size.x, pos.y); } - result += m_lines[lines.m_column].substr(0, columns.m_column, Line::LinePart::Utf8); + auto ret = window->DC.CursorPos; + ret.y += size.y; + return ret; } - return result; -} + void TextEditor::setLanguageDefinition(const LanguageDefinition &languageDef) { + m_languageDefinition = languageDef; + m_regexList.clear(); -TextEditor::Coordinates TextEditor::setCoordinates(int32_t line, int32_t column) const { - if (isEmpty()) - return Coordinates(0, 0); + for (auto &r: m_languageDefinition.m_tokenRegexStrings) + m_regexList.push_back(std::make_pair(std::regex(r.first, std::regex_constants::optimize), r.second)); - Coordinates result = Coordinates(0, 0); - auto lineCount = (int32_t)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 = getLineMaxColumn(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; -} - -TextEditor::Coordinates TextEditor::setCoordinates(const Coordinates &value) const { - auto sanitized_value = setCoordinates(value.m_line, value.m_column); - return sanitized_value; -} - -TextEditor::Selection TextEditor::setCoordinates(const Selection &value) const { - auto start = setCoordinates(value.m_start); - auto end = setCoordinates(value.m_end); - if (start == Invalid || end == Invalid) - return Selection(Invalid, Invalid); - if (start > end) { - std::swap(start, end); + colorize(); + } + + const TextEditor::Palette &TextEditor::getPalette() { return s_paletteBase; } + + void TextEditor::setPalette(const Palette &value) { + s_paletteBase = value; + } + + bool TextEditor::isEndOfLine(const Coordinates &coordinates) const { + if (coordinates.m_line < (int32_t) m_lines.size()) + return coordinates.m_column >= getStringCharacterCount(m_lines[coordinates.m_line].m_chars); + return true; + } + + bool TextEditor::isEndOfFile(const Coordinates &coordinates) const { + if (coordinates.m_line < (int32_t) m_lines.size()) + return coordinates.m_line >= (int32_t) m_lines.size() - 1 && isEndOfLine(coordinates); + return true; + } + + std::string TextEditor::getText(const Selection &from) const { + std::string result = ""; + auto selection = setCoordinates(from); + auto columns = selection.getSelectedColumns(); + if (selection.isSingleLine()) + result = m_lines[selection.m_start.m_line].substr(columns.m_line, columns.m_column, Line::LinePart::Utf8); + else { + auto lines = selection.getSelectedLines(); + result = m_lines[lines.m_line].substr(columns.m_line, -1, Line::LinePart::Utf8) + '\n'; + for (uint64_t i = lines.m_line + 1; i < lines.m_column; i++) { + result += m_lines[i].m_chars + '\n'; + } + result += m_lines[lines.m_column].substr(0, columns.m_column, Line::LinePart::Utf8); + } + + return result; + } + + TextEditor::Coordinates TextEditor::setCoordinates(int32_t line, int32_t column) const { + if (isEmpty()) + return Coordinates(0, 0); + + Coordinates result = Coordinates(0, 0); + auto lineCount = (int32_t) 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 = getLineMaxColumn(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; + } + + TextEditor::Coordinates TextEditor::setCoordinates(const Coordinates &value) const { + auto sanitized_value = setCoordinates(value.m_line, value.m_column); + return sanitized_value; + } + + TextEditor::Selection TextEditor::setCoordinates(const Selection &value) const { + auto start = setCoordinates(value.m_start); + auto end = setCoordinates(value.m_end); + if (start == Invalid || end == Invalid) + return Selection(Invalid, Invalid); + if (start > end) { + std::swap(start, end); + } + return Selection(start, end); } - return Selection(start, end); -} // "Borrowed" from ImGui source -static inline void imTextCharToUtf8(std::string &buffer, uint32_t c) { - if (c < 0x80) { - buffer += (char) c; - return; - } - if (c < 0x800) { - buffer += (char)(0xc0 + (c >> 6)); - buffer += (char)(0x80 + (c & 0x3f)); - return; - } - if (c >= 0xdc00 && c < 0xe000) - return; - if (c >= 0xd800 && c < 0xdc00) { - buffer += (char)(0xf0 + (c >> 18)); - buffer += (char)(0x80 + ((c >> 12) & 0x3f)); - buffer += (char)(0x80 + ((c >> 6) & 0x3f)); - buffer += (char)(0x80 + ((c)&0x3f)); - return; - } - // else if (c < 0x10000) - { - buffer += (char)(0xe0 + (c >> 12)); - buffer += (char)(0x80 + ((c >> 6) & 0x3f)); - buffer += (char)(0x80 + ((c)&0x3f)); - return; - } -} - -static inline int32_t imTextCharToUtf8(char *buffer, int32_t buf_size, uint32_t c) { - std::string input; - imTextCharToUtf8(input, c); - auto size = input.size(); - int32_t i=0; - for (;i> 6)); + buffer += (char) (0x80 + (c & 0x3f)); + return; + } + if (c >= 0xdc00 && c < 0xe000) + return; + if (c >= 0xd800 && c < 0xdc00) { + buffer += (char) (0xf0 + (c >> 18)); + buffer += (char) (0x80 + ((c >> 12) & 0x3f)); + buffer += (char) (0x80 + ((c >> 6) & 0x3f)); + buffer += (char) (0x80 + ((c) & 0x3f)); + return; + } + // else if (c < 0x10000) + { + buffer += (char) (0xe0 + (c >> 12)); + buffer += (char) (0x80 + ((c >> 6) & 0x3f)); + buffer += (char) (0x80 + ((c) & 0x3f)); + return; } - if (lines.m_line < lines.m_column) - removeLine(lines.m_line + 1, lines.m_column); } - m_textChanged = true; -} - -void TextEditor::appendLine(const std::string &value) { - auto text = replaceStrings(preprocessText(value), "\000", "."); - if (text.empty()) - return; - if (isEmpty()) { - m_lines[0].m_chars = text; - m_lines[0].m_colors = std::string(text.size(), 0); - m_lines[0].m_flags = std::string(text.size(), 0); - } else - m_lines.push_back(Line(text)); - - setCursorPosition(setCoordinates((int32_t) m_lines.size() - 1, 0)); - m_lines.back().m_colorized = false; - ensureCursorVisible(); - m_textChanged = true; -} - - -int32_t TextEditor::insertTextAt(Coordinates /* inout */ &where, const std::string &value) { - if (value.empty()) - return 0; - auto start = setCoordinates(where); - if (start == Invalid) - return 0; - auto &line = m_lines[start.m_line]; - auto stringVector = splitString(value, "\n", false); - auto lineCount = (int32_t)stringVector.size(); - where.m_line += lineCount - 1; - where.m_column += getStringCharacterCount(stringVector[lineCount - 1]); - stringVector[lineCount - 1].append(line.substr(start.m_column,-1,Line::LinePart::Utf8)); - line.erase(start.m_column, -1); - - line.append(stringVector[0]); - line.m_colorized = false; - for (int32_t i = 1; i < lineCount; i++) { - insertLine(start.m_line + i, stringVector[i]); - m_lines[start.m_line + i].m_colorized = false; + static inline int32_t imTextCharToUtf8(char *buffer, int32_t buf_size, uint32_t c) { + std::string input; + imTextCharToUtf8(input, c); + auto size = input.size(); + int32_t i = 0; + for (; i < size; i++) + buffer[i] = input[i]; + buffer[i] = 0; + return size; } - m_textChanged = true; - return lineCount; -} -void TextEditor::addUndo(UndoRecord &value) { - if (m_readOnly) - return; - - m_undoBuffer.resize((uint64_t)(m_undoIndex + 1)); - m_undoBuffer.back() = value; - ++m_undoIndex; -} - -TextEditor::Coordinates TextEditor::screenPosToCoordinates(const ImVec2 &position) const { - ImVec2 local = position - ImGui::GetCursorScreenPos(); - int32_t lineNo = std::max(0, (int32_t)floor(local.y / m_charAdvance.y)); - if (local.x < (m_leftMargin - 2) || lineNo >= (int32_t)m_lines.size() || m_lines[lineNo].empty()) - return setCoordinates(std::min(lineNo, (int32_t)m_lines.size() - 1), 0); - std::string line = m_lines[lineNo].m_chars; - local.x -= (m_leftMargin - 5); - int32_t count = 0; - uint64_t length; - int32_t increase; - do { - increase = utf8CharLength(line[count]); - count += increase; - std::string partialLine = line.substr(0, count); - length = ImGui::CalcTextSize(partialLine.c_str(), nullptr, false, m_charAdvance.x * count).x; - } while (length < local.x && count < (int32_t)line.size() + increase); - - auto result = getCharacterCoordinates(lineNo, count - increase); - result = setCoordinates(result); - if (result == Invalid) - return Coordinates(0, 0); - return result; -} - -void TextEditor::deleteWordLeft() { - const auto wordEnd = getCursorPosition(); - const auto wordStart = findPreviousWord(getCursorPosition()); - setSelection(Selection(wordStart, wordEnd)); - backspace(); -} - -void TextEditor::deleteWordRight() { - const auto wordStart = getCursorPosition(); - const auto wordEnd = findNextWord(getCursorPosition()); - setSelection(Selection(wordStart, wordEnd)); - backspace(); -} - -bool isWordChar(char c) { - auto asUChar = static_cast(c); - return std::isalnum(asUChar) || c == '_' || asUChar > 0x7F; -} - -TextEditor::Coordinates TextEditor::findWordStart(const Coordinates &from) const { - Coordinates at = setCoordinates(from); - if (at.m_line >= (int32_t)m_lines.size()) - return at; - - auto &line = m_lines[at.m_line]; - auto charIndex = lineCoordinateToIndex(at); - - if (isWordChar(line.m_chars[charIndex])) { - while (charIndex > 0 && isWordChar(line.m_chars[charIndex - 1])) - --charIndex; - } else if (ispunct(line.m_chars[charIndex])) { - while (charIndex > 0 && ispunct(line.m_chars[charIndex - 1])) - --charIndex; - } else if (isspace(line.m_chars[charIndex])) { - while (charIndex > 0 && isspace(line.m_chars[charIndex - 1])) - --charIndex; + void TextEditor::advance(Coordinates &coordinates) const { + if (isEndOfFile(coordinates)) + return; + if (isEndOfLine(coordinates)) { + ++coordinates.m_line; + coordinates.m_column = 0; + return; + } + auto &line = m_lines[coordinates.m_line]; + int32_t column = coordinates.m_column; + std::string lineChar = line[column]; + auto incr = getStringCharacterCount(lineChar); + coordinates.m_column += incr; } - return getCharacterCoordinates(at.m_line, charIndex); -} -TextEditor::Coordinates TextEditor::findWordEnd(const Coordinates &from) const { - Coordinates at = from; - if (at.m_line >= (int32_t)m_lines.size()) - return at; + void TextEditor::deleteRange(const Selection &rangeToDelete) { + if (m_readOnly) + return; + Selection selection = setCoordinates(rangeToDelete); + auto columns = selection.getSelectedColumns(); - auto &line = m_lines[at.m_line]; - auto charIndex = lineCoordinateToIndex(at); + if (selection.isSingleLine()) { + auto &line = m_lines[selection.m_start.m_line]; + line.erase(columns.m_line, columns.m_column); + } else { + auto lines = selection.getSelectedLines(); + auto &firstLine = m_lines[lines.m_line]; + auto &lastLine = m_lines[lines.m_column]; + firstLine.erase(columns.m_line, -1); + lastLine.erase(0, columns.m_column); - if (isWordChar(line.m_chars[charIndex])) { - while (charIndex < (int32_t)line.m_chars.size() && isWordChar(line.m_chars[charIndex])) - ++charIndex; - } else if (ispunct(line.m_chars[charIndex])) { - while (charIndex < (int32_t)line.m_chars.size() && ispunct(line.m_chars[charIndex])) - ++charIndex; - } else if (isspace(line.m_chars[charIndex])) { - while (charIndex < (int32_t)line.m_chars.size() && isspace(line.m_chars[charIndex])) - ++charIndex; + if (!lastLine.empty()) { + firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end()); + firstLine.m_colorized = false; + } + if (lines.m_line < lines.m_column) + removeLine(lines.m_line + 1, lines.m_column); + } + + m_textChanged = true; } - return getCharacterCoordinates(at.m_line, charIndex); -} -TextEditor::Coordinates TextEditor::findNextWord(const Coordinates &from) const { - Coordinates at = from; - if (at.m_line >= (int32_t)m_lines.size()) - return at; + void TextEditor::appendLine(const std::string &value) { + auto text = replaceStrings(preprocessText(value), "\000", "."); + if (text.empty()) + return; + if (isEmpty()) { + m_lines[0].m_chars = text; + m_lines[0].m_colors = std::string(text.size(), 0); + m_lines[0].m_flags = std::string(text.size(), 0); + } else + m_lines.push_back(Line(text)); - auto &line = m_lines[at.m_line]; - auto charIndex = lineCoordinateToIndex(at); - - if (isspace(line.m_chars[charIndex])) { - while (charIndex < (int32_t)line.m_chars.size() && isspace(line.m_chars[charIndex])) - ++charIndex; + setCursorPosition(setCoordinates((int32_t) m_lines.size() - 1, 0)); + m_lines.back().m_colorized = false; + ensureCursorVisible(); + m_textChanged = true; } - if (isWordChar(line.m_chars[charIndex])) { - while (charIndex < (int32_t)line.m_chars.size() && (isWordChar(line.m_chars[charIndex]))) - ++charIndex; - } else if (ispunct(line.m_chars[charIndex])) { - while (charIndex < (int32_t)line.m_chars.size() && (ispunct(line.m_chars[charIndex]))) - ++charIndex; + + + int32_t TextEditor::insertTextAt(Coordinates /* inout */ &where, const std::string &value) { + if (value.empty()) + return 0; + auto start = setCoordinates(where); + if (start == Invalid) + return 0; + auto &line = m_lines[start.m_line]; + auto stringVector = splitString(value, "\n", false); + auto lineCount = (int32_t) stringVector.size(); + where.m_line += lineCount - 1; + where.m_column += getStringCharacterCount(stringVector[lineCount - 1]); + stringVector[lineCount - 1].append(line.substr(start.m_column, -1, Line::LinePart::Utf8)); + line.erase(start.m_column, -1); + + line.append(stringVector[0]); + line.m_colorized = false; + for (int32_t i = 1; i < lineCount; i++) { + insertLine(start.m_line + i, stringVector[i]); + m_lines[start.m_line + i].m_colorized = false; + } + m_textChanged = true; + return lineCount; } - return getCharacterCoordinates(at.m_line, charIndex); -} -TextEditor::Coordinates TextEditor::findPreviousWord(const Coordinates &from) const { - Coordinates at = from; - if (at.m_line >= (int32_t)m_lines.size()) - return at; + void TextEditor::addUndo(UndoRecord &value) { + if (m_readOnly) + return; - auto &line = m_lines[at.m_line]; - auto charIndex = lineCoordinateToIndex(at); - - if (isspace(line.m_chars[charIndex - 1])) { - while (charIndex > 0 && isspace(line.m_chars[charIndex - 1])) - --charIndex; + m_undoBuffer.resize((uint64_t) (m_undoIndex + 1)); + m_undoBuffer.back() = value; + ++m_undoIndex; } - if (isWordChar(line.m_chars[charIndex - 1])) { - while (charIndex > 0 && isWordChar(line.m_chars[charIndex - 1])) - --charIndex; - } else if (ispunct(line.m_chars[charIndex - 1])) { - while (charIndex > 0 && ispunct(line.m_chars[charIndex - 1])) - --charIndex; + + TextEditor::Coordinates TextEditor::screenPosToCoordinates(const ImVec2 &position) const { + ImVec2 local = position - ImGui::GetCursorScreenPos(); + int32_t lineNo = std::max(0, (int32_t) floor(local.y / m_charAdvance.y)); + if (local.x < (m_leftMargin - 2) || lineNo >= (int32_t) m_lines.size() || m_lines[lineNo].empty()) + return setCoordinates(std::min(lineNo, (int32_t) m_lines.size() - 1), 0); + std::string line = m_lines[lineNo].m_chars; + local.x -= (m_leftMargin - 5); + int32_t count = 0; + uint64_t length; + int32_t increase; + do { + increase = utf8CharLength(line[count]); + count += increase; + std::string partialLine = line.substr(0, count); + length = ImGui::CalcTextSize(partialLine.c_str(), nullptr, false, m_charAdvance.x * count).x; + } while (length < local.x && count < (int32_t) line.size() + increase); + + auto result = getCharacterCoordinates(lineNo, count - increase); + result = setCoordinates(result); + if (result == Invalid) + return Coordinates(0, 0); + return result; } - return getCharacterCoordinates(at.m_line, charIndex); -} -static TextEditor::Coordinates stringIndexToCoordinates(int32_t strIndex, const std::string &input ) { - if (strIndex < 0 || strIndex > (int32_t)input.size()) - return TextEditor::Coordinates(0, 0); - std::string str = input.substr(0, strIndex); - auto line = std::count(str.begin(),str.end(),'\n'); - auto index = str.find_last_of('\n'); - str = str.substr(index+1); - auto col = getStringCharacterCount(str); - - return TextEditor::Coordinates(line, col); -} - -static int32_t utf8CharCount(const std::string &line, int32_t start, int32_t numChars) { - if (line.empty()) - return 0; - - int32_t index = 0; - for (int32_t column = 0; start + index < line.size() && column < numChars; ++column) - index += utf8CharLength(line[start + index]); - - return index; -} - - -int32_t TextEditor::lineCoordinateToIndex(const Coordinates &coordinates) const { - if (coordinates.m_line >= m_lines.size()) - return -1; - - const auto &line = m_lines[coordinates.m_line]; - return utf8CharCount(line.m_chars, 0, coordinates.m_column); -} - -int32_t TextEditor::Line::getCharacterColumn(int32_t index) const { - int32_t col = 0; - int32_t i = 0; - while (i < index && i < (int32_t)size()) { - auto c = m_chars[i]; - i += utf8CharLength(c); - col++; + void TextEditor::deleteWordLeft() { + const auto wordEnd = getCursorPosition(); + const auto wordStart = findPreviousWord(getCursorPosition()); + setSelection(Selection(wordStart, wordEnd)); + backspace(); } - return col; -} -TextEditor::Coordinates TextEditor::getCharacterCoordinates(int32_t lineIndex, int32_t strIndex) const { - if (lineIndex < 0 || lineIndex >= (int32_t)m_lines.size()) - return Coordinates(0, 0); - auto &line = m_lines[lineIndex]; - return setCoordinates(lineIndex, line.getCharacterColumn(strIndex)); -} - -uint64_t TextEditor::getLineByteCount(int32_t lineIndex) const { - if (lineIndex >= m_lines.size() || lineIndex < 0) - return 0; - - auto &line = m_lines[lineIndex]; - return line.size(); -} - -int32_t TextEditor::getLineCharacterCount(int32_t line) const { - return getLineMaxColumn(line); -} - -int32_t TextEditor::getLineMaxColumn(int32_t line) const{ - if (line >= m_lines.size() || line < 0) - return 0; - return getStringCharacterCount(m_lines[line].m_chars); -} - -void TextEditor::removeLine(int32_t lineStart, int32_t lineEnd) { - - ErrorMarkers errorMarker; - for (auto &i : m_errorMarkers) { - ErrorMarkers::value_type e(i.first.m_line >= lineStart ? setCoordinates(i.first.m_line - 1, i.first.m_column ) : i.first, i.second); - if (e.first.m_line >= lineStart && e.first.m_line <= lineEnd) - continue; - errorMarker.insert(e); + void TextEditor::deleteWordRight() { + const auto wordStart = getCursorPosition(); + const auto wordEnd = findNextWord(getCursorPosition()); + setSelection(Selection(wordStart, wordEnd)); + backspace(); } - m_errorMarkers = std::move(errorMarker); - Breakpoints breakpoints; - for (auto breakpoint : m_breakpoints) { - if (breakpoint <= lineStart || breakpoint >= lineEnd) { - if (breakpoint >= lineEnd) { - breakpoints.insert(breakpoint - 1); + bool isWordChar(char c) { + auto asUChar = static_cast(c); + return std::isalnum(asUChar) || c == '_' || asUChar > 0x7F; + } + + TextEditor::Coordinates TextEditor::findWordStart(const Coordinates &from) const { + Coordinates at = setCoordinates(from); + if (at.m_line >= (int32_t) m_lines.size()) + return at; + + auto &line = m_lines[at.m_line]; + auto charIndex = lineCoordinateToIndex(at); + + if (isWordChar(line.m_chars[charIndex])) { + while (charIndex > 0 && isWordChar(line.m_chars[charIndex - 1])) + --charIndex; + } else if (ispunct(line.m_chars[charIndex])) { + while (charIndex > 0 && ispunct(line.m_chars[charIndex - 1])) + --charIndex; + } else if (isspace(line.m_chars[charIndex])) { + while (charIndex > 0 && isspace(line.m_chars[charIndex - 1])) + --charIndex; + } + return getCharacterCoordinates(at.m_line, charIndex); + } + + TextEditor::Coordinates TextEditor::findWordEnd(const Coordinates &from) const { + Coordinates at = from; + if (at.m_line >= (int32_t) m_lines.size()) + return at; + + auto &line = m_lines[at.m_line]; + auto charIndex = lineCoordinateToIndex(at); + + if (isWordChar(line.m_chars[charIndex])) { + while (charIndex < (int32_t) line.m_chars.size() && isWordChar(line.m_chars[charIndex])) + ++charIndex; + } else if (ispunct(line.m_chars[charIndex])) { + while (charIndex < (int32_t) line.m_chars.size() && ispunct(line.m_chars[charIndex])) + ++charIndex; + } else if (isspace(line.m_chars[charIndex])) { + while (charIndex < (int32_t) line.m_chars.size() && isspace(line.m_chars[charIndex])) + ++charIndex; + } + return getCharacterCoordinates(at.m_line, charIndex); + } + + TextEditor::Coordinates TextEditor::findNextWord(const Coordinates &from) const { + Coordinates at = from; + if (at.m_line >= (int32_t) m_lines.size()) + return at; + + auto &line = m_lines[at.m_line]; + auto charIndex = lineCoordinateToIndex(at); + + if (isspace(line.m_chars[charIndex])) { + while (charIndex < (int32_t) line.m_chars.size() && isspace(line.m_chars[charIndex])) + ++charIndex; + } + if (isWordChar(line.m_chars[charIndex])) { + while (charIndex < (int32_t) line.m_chars.size() && (isWordChar(line.m_chars[charIndex]))) + ++charIndex; + } else if (ispunct(line.m_chars[charIndex])) { + while (charIndex < (int32_t) line.m_chars.size() && (ispunct(line.m_chars[charIndex]))) + ++charIndex; + } + return getCharacterCoordinates(at.m_line, charIndex); + } + + TextEditor::Coordinates TextEditor::findPreviousWord(const Coordinates &from) const { + Coordinates at = from; + if (at.m_line >= (int32_t) m_lines.size()) + return at; + + auto &line = m_lines[at.m_line]; + auto charIndex = lineCoordinateToIndex(at); + + if (isspace(line.m_chars[charIndex - 1])) { + while (charIndex > 0 && isspace(line.m_chars[charIndex - 1])) + --charIndex; + } + if (isWordChar(line.m_chars[charIndex - 1])) { + while (charIndex > 0 && isWordChar(line.m_chars[charIndex - 1])) + --charIndex; + } else if (ispunct(line.m_chars[charIndex - 1])) { + while (charIndex > 0 && ispunct(line.m_chars[charIndex - 1])) + --charIndex; + } + return getCharacterCoordinates(at.m_line, charIndex); + } + + static TextEditor::Coordinates stringIndexToCoordinates(int32_t strIndex, const std::string &input) { + if (strIndex < 0 || strIndex > (int32_t) input.size()) + return TextEditor::Coordinates(0, 0); + std::string str = input.substr(0, strIndex); + auto line = std::count(str.begin(), str.end(), '\n'); + auto index = str.find_last_of('\n'); + str = str.substr(index + 1); + auto col = getStringCharacterCount(str); + + return TextEditor::Coordinates(line, col); + } + + static int32_t utf8CharCount(const std::string &line, int32_t start, int32_t numChars) { + if (line.empty()) + return 0; + + int32_t index = 0; + for (int32_t column = 0; start + index < line.size() && column < numChars; ++column) + index += utf8CharLength(line[start + index]); + + return index; + } + + + int32_t TextEditor::lineCoordinateToIndex(const Coordinates &coordinates) const { + if (coordinates.m_line >= m_lines.size()) + return -1; + + const auto &line = m_lines[coordinates.m_line]; + return utf8CharCount(line.m_chars, 0, coordinates.m_column); + } + + int32_t TextEditor::Line::getCharacterColumn(int32_t index) const { + int32_t col = 0; + int32_t i = 0; + while (i < index && i < (int32_t) size()) { + auto c = m_chars[i]; + i += utf8CharLength(c); + col++; + } + return col; + } + + TextEditor::Coordinates TextEditor::getCharacterCoordinates(int32_t lineIndex, int32_t strIndex) const { + if (lineIndex < 0 || lineIndex >= (int32_t) m_lines.size()) + return Coordinates(0, 0); + auto &line = m_lines[lineIndex]; + return setCoordinates(lineIndex, line.getCharacterColumn(strIndex)); + } + + uint64_t TextEditor::getLineByteCount(int32_t lineIndex) const { + if (lineIndex >= m_lines.size() || lineIndex < 0) + return 0; + + auto &line = m_lines[lineIndex]; + return line.size(); + } + + int32_t TextEditor::getLineCharacterCount(int32_t line) const { + return getLineMaxColumn(line); + } + + int32_t TextEditor::getLineMaxColumn(int32_t line) const { + if (line >= m_lines.size() || line < 0) + return 0; + return getStringCharacterCount(m_lines[line].m_chars); + } + + void TextEditor::removeLine(int32_t lineStart, int32_t lineEnd) { + + ErrorMarkers errorMarker; + for (auto &i: m_errorMarkers) { + ErrorMarkers::value_type e(i.first.m_line >= lineStart ? setCoordinates(i.first.m_line - 1, i.first.m_column) : i.first, i.second); + if (e.first.m_line >= lineStart && e.first.m_line <= lineEnd) + continue; + errorMarker.insert(e); + } + m_errorMarkers = std::move(errorMarker); + + Breakpoints breakpoints; + for (auto breakpoint: m_breakpoints) { + if (breakpoint <= lineStart || breakpoint >= lineEnd) { + if (breakpoint >= lineEnd) { + breakpoints.insert(breakpoint - 1); + m_breakPointsChanged = true; + } else + breakpoints.insert(breakpoint); + } + } + + m_breakpoints = std::move(breakpoints); + // use clamp to ensure valid results instead of assert. + auto start = std::clamp(lineStart, 0, (int32_t) m_lines.size() - 1); + auto end = std::clamp(lineEnd, 0, (int32_t) m_lines.size()); + if (start > end) + std::swap(start, end); + + m_lines.erase(m_lines.begin() + lineStart, m_lines.begin() + lineEnd + 1); + + m_textChanged = true; + } + + void TextEditor::removeLine(int32_t index) { + removeLine(index, index); + } + + void TextEditor::insertLine(int32_t index, const std::string &text) { + if (index < 0 || index > (int32_t) m_lines.size()) + return; + auto &line = insertLine(index); + line.append(text); + line.m_colorized = false; + m_textChanged = true; + } + + TextEditor::Line &TextEditor::insertLine(int32_t index) { + + if (isEmpty()) + return *m_lines.insert(m_lines.begin(), Line()); + + if (index == m_lines.size()) + return *m_lines.insert(m_lines.end(), Line()); + + auto newLine = Line(); + + TextEditor::Line &result = *m_lines.insert(m_lines.begin() + index, newLine); + result.m_colorized = false; + + ErrorMarkers errorMarker; + for (auto &i: m_errorMarkers) + errorMarker.insert(ErrorMarkers::value_type(i.first.m_line >= index ? setCoordinates(i.first.m_line + 1, i.first.m_column) : i.first, i.second)); + m_errorMarkers = std::move(errorMarker); + + Breakpoints breakpoints; + for (auto breakpoint: m_breakpoints) { + if (breakpoint >= index) { + breakpoints.insert(breakpoint + 1); m_breakPointsChanged = true; } else breakpoints.insert(breakpoint); } - } + if (m_breakPointsChanged) + m_breakpoints = std::move(breakpoints); - m_breakpoints = std::move(breakpoints); - // use clamp to ensure valid results instead of assert. - auto start = std::clamp(lineStart, 0, (int32_t)m_lines.size() - 1); - auto end = std::clamp(lineEnd, 0, (int32_t)m_lines.size()); - if (start > end) - std::swap(start, end); - - m_lines.erase(m_lines.begin() + lineStart, m_lines.begin() + lineEnd + 1); - - m_textChanged = true; -} - -void TextEditor::removeLine(int32_t index) { - removeLine(index, index); -} - -void TextEditor::insertLine(int32_t index, const std::string &text) { - if (index < 0 || index > (int32_t)m_lines.size()) - return; - auto &line = insertLine(index); - line.append(text); - line.m_colorized = false; - m_textChanged = true; -} - -TextEditor::Line &TextEditor::insertLine(int32_t index) { - - if (isEmpty()) - return *m_lines.insert(m_lines.begin(), Line()); - - if (index == m_lines.size()) - return *m_lines.insert(m_lines.end(), Line()); - - auto newLine = Line(); - - TextEditor::Line &result = *m_lines.insert(m_lines.begin() + index, newLine); - result.m_colorized = false; - - ErrorMarkers errorMarker; - for (auto &i : m_errorMarkers) - errorMarker.insert(ErrorMarkers::value_type(i.first.m_line >= index ? setCoordinates(i.first.m_line + 1, i.first.m_column) : i.first, i.second)); - m_errorMarkers = std::move(errorMarker); - - Breakpoints breakpoints; - for (auto breakpoint : m_breakpoints) { - if (breakpoint >= index) { - breakpoints.insert(breakpoint + 1); - m_breakPointsChanged = true; - } else - breakpoints.insert(breakpoint); - } - if (m_breakPointsChanged) - m_breakpoints = std::move(breakpoints); - - return result; -} - -TextEditor::PaletteIndex TextEditor::getColorIndexFromFlags(Line::Flags flags) { - if (flags.m_bits.globalDocComment) - return PaletteIndex::GlobalDocComment; - if (flags.m_bits.blockDocComment ) - return PaletteIndex::DocBlockComment; - if (flags.m_bits.docComment) - return PaletteIndex::DocComment; - if (flags.m_bits.blockComment) - return PaletteIndex::BlockComment; - if (flags.m_bits.comment) - return PaletteIndex::Comment; - if (flags.m_bits.deactivated) - return PaletteIndex::PreprocessorDeactivated; - if (flags.m_bits.preprocessor) - return PaletteIndex::Directive; - return PaletteIndex::Default; -} - -void TextEditor::handleKeyboardInputs() { - ImGuiIO &io = ImGui::GetIO(); - - // command => Ctrl - // control => Super - // option => Alt - auto ctrl = io.KeyCtrl; - auto alt = io.KeyAlt; - auto shift = io.KeyShift; - - if (ImGui::IsWindowFocused()) { - if (ImGui::IsWindowHovered()) - ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); - - io.WantCaptureKeyboard = true; - io.WantTextInput = true; - - if (!isReadOnly() && !ctrl && !shift && !alt && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) - enterCharacter('\n', false); - else if (!isReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_Tab)) - enterCharacter('\t', shift); - - if (!isReadOnly() && !io.InputQueueCharacters.empty()) { - for (int32_t i = 0; i < io.InputQueueCharacters.Size; i++) { - auto c = io.InputQueueCharacters[i]; - if (c != 0 && (c == '\n' || c >= 32)) { - enterCharacter(c, shift); - } - } - io.InputQueueCharacters.resize(0); - } - } -} - -void TextEditor::handleMouseInputs() { - ImGuiIO &io = ImGui::GetIO(); - auto shift = io.KeyShift; - auto ctrl = io.ConfigMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl; - auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; - - if (ImGui::IsWindowHovered()) { - if (!alt) { - auto click = ImGui::IsMouseClicked(0); - auto doubleClick = ImGui::IsMouseDoubleClicked(0); - auto rightClick = ImGui::IsMouseClicked(1); - auto t = ImGui::GetTime(); - auto tripleClick = click && !doubleClick && (m_lastClick != -1.0f && (t - m_lastClick) < io.MouseDoubleClickTime); - bool resetBlinking = false; - /* - Left mouse button triple click - */ - - if (tripleClick) { - if (!ctrl) { - 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, getLineMaxColumn(line)); - } - - m_lastClick = -1.0f; - resetBlinking=true; - } - - /* - Left mouse button double click - */ - - else if (doubleClick) { - if (!ctrl) { - m_state.m_cursorPosition = screenPosToCoordinates(ImGui::GetMousePos()); - m_state.m_selection.m_start = findWordStart(m_state.m_cursorPosition); - m_state.m_selection.m_end = findWordEnd(m_state.m_cursorPosition); - } - - m_lastClick = (float)ImGui::GetTime(); - resetBlinking=true; - } - - /* - Left mouse button click - */ - else if (click) { - if (ctrl) { - m_state.m_cursorPosition = m_interactiveSelection.m_start = m_interactiveSelection.m_end = screenPosToCoordinates( - ImGui::GetMousePos()); - selectWordUnderCursor(); - } else if (shift) { - m_interactiveSelection.m_end = screenPosToCoordinates(ImGui::GetMousePos()); - m_state.m_cursorPosition = m_interactiveSelection.m_end; - setSelection(m_interactiveSelection); - } else { - m_state.m_cursorPosition = m_interactiveSelection.m_start = m_interactiveSelection.m_end = screenPosToCoordinates( - ImGui::GetMousePos()); - setSelection(m_interactiveSelection); - } - resetCursorBlinkTime(); - - ensureCursorVisible(); - m_lastClick = (float)ImGui::GetTime(); - } else if (rightClick) { - auto cursorPosition = screenPosToCoordinates(ImGui::GetMousePos()); - - if (!hasSelection() || m_state.m_selection.m_start > cursorPosition || cursorPosition > m_state.m_selection.m_end) { - m_state.m_cursorPosition = m_interactiveSelection.m_start = m_interactiveSelection.m_end = cursorPosition; - setSelection(m_interactiveSelection); - } - resetCursorBlinkTime(); - m_raiseContextMenu = true; - ImGui::SetWindowFocus(); - } - // Mouse left button dragging (=> update selection) - else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) { - io.WantCaptureMouse = true; - m_state.m_cursorPosition = m_interactiveSelection.m_end = screenPosToCoordinates(ImGui::GetMousePos()); - setSelection(m_interactiveSelection); - resetBlinking=true; - } - if (resetBlinking) - resetCursorBlinkTime(); - } - } -} - -inline void TextUnformattedColoredAt(const ImVec2 &pos, const ImU32 &color, const char *text) { - ImGui::SetCursorScreenPos(pos); - ImGui::PushStyleColor(ImGuiCol_Text,color); - ImGui::TextUnformatted(text); - ImGui::PopStyleColor(); -} - -uint32_t TextEditor::skipSpaces(const Coordinates &from) { - auto line = from.m_line; - if (line >= m_lines.size()) - return 0; - auto &lines = m_lines[line].m_chars; - auto &colors = m_lines[line].m_colors; - auto charIndex = lineCoordinateToIndex(from); - uint32_t s = 0; - while (charIndex < (int32_t)lines.size() && lines[charIndex] == ' ' && colors[charIndex] == 0x00) { - ++s; - ++charIndex; - } - if (m_updateFocus) - setFocus(); - return s; -} - -void TextEditor::setFocus() { - m_state.m_cursorPosition = m_focusAtCoords; - resetCursorBlinkTime(); - ensureCursorVisible(); - if (!this->m_readOnly) - ImGui::SetKeyboardFocusHere(0); - m_updateFocus = false; -} - -bool TextEditor::MatchedBracket::checkPosition(TextEditor *editor, const Coordinates &from) { - auto lineIndex = from.m_line; - auto line = editor->m_lines[lineIndex].m_chars; - auto colors = editor->m_lines[lineIndex].m_colors; - if (!line.empty() && colors.empty()) - return false; - auto result = editor->lineCoordinateToIndex(from); - auto character = line[result]; - auto color = colors[result]; - if (s_separators.find(character) != std::string::npos && (static_cast(color) == PaletteIndex::Separator || static_cast(color) == PaletteIndex::WarningText) || - s_operators.find(character) != std::string::npos && (static_cast(color) == PaletteIndex::Operator || static_cast(color) == PaletteIndex::WarningText)) { - if (m_nearCursor != editor->getCharacterCoordinates(lineIndex, result)) { - m_nearCursor = editor->getCharacterCoordinates(lineIndex, result); - m_changed = true; - } - m_active = true; - return true; - } - return false; -} - -int32_t TextEditor::MatchedBracket::detectDirection(TextEditor *editor, const Coordinates &from) { - std::string brackets = "()[]{}<>"; - int32_t result = -2; // dont check either - auto start = editor->setCoordinates(from); - if (start == TextEditor::Invalid) return result; - auto lineIndex = start.m_line; - auto line = editor->m_lines[lineIndex].m_chars; - auto charIndex = editor->lineCoordinateToIndex(start); - auto ch2 = line[charIndex]; - auto idx2 = brackets.find(ch2); - if (charIndex == 0) {// no previous character - if (idx2 == std::string::npos) // no brackets - return -2;// dont check either - else - return 1; // check only current - }// charIndex > 0 - auto ch1 = line[charIndex-1]; - auto idx1 = brackets.find(ch1); - if (idx1 == std::string::npos && idx2 == std::string::npos) // no brackets - return -2; - if (idx1 != std::string::npos && idx2 != std::string::npos) { - if (idx1 % 2) // closing bracket + any bracket - return -1; // check both and previous first - else if (!(idx1 % 2) && !(idx2 % 2)) // opening bracket + opening bracket - return 0; // check both and current first - } else if (idx1 != std::string::npos) // only first bracket - return 1; // check only previous - else if (idx2 != std::string::npos) // only second bracket + } + + TextEditor::PaletteIndex TextEditor::getColorIndexFromFlags(Line::Flags flags) { + if (flags.m_bits.globalDocComment) + return PaletteIndex::GlobalDocComment; + if (flags.m_bits.blockDocComment) + return PaletteIndex::DocBlockComment; + if (flags.m_bits.docComment) + return PaletteIndex::DocComment; + if (flags.m_bits.blockComment) + return PaletteIndex::BlockComment; + if (flags.m_bits.comment) + return PaletteIndex::Comment; + if (flags.m_bits.deactivated) + return PaletteIndex::PreprocessorDeactivated; + if (flags.m_bits.preprocessor) + return PaletteIndex::Directive; + return PaletteIndex::Default; + } + + void TextEditor::handleKeyboardInputs() { + ImGuiIO &io = ImGui::GetIO(); + + // command => Ctrl + // control => Super + // option => Alt + auto ctrl = io.KeyCtrl; + auto alt = io.KeyAlt; + auto shift = io.KeyShift; + + if (ImGui::IsWindowFocused()) { + if (ImGui::IsWindowHovered()) + ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); + + io.WantCaptureKeyboard = true; + io.WantTextInput = true; + + if (!m_readOnly && !ctrl && !shift && !alt && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) + enterCharacter('\n', false); + else if (!m_readOnly && !ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_Tab)) + enterCharacter('\t', shift); + + if (!m_readOnly && !io.InputQueueCharacters.empty()) { + for (int32_t i = 0; i < io.InputQueueCharacters.Size; i++) { + auto c = io.InputQueueCharacters[i]; + if (c != 0 && (c == '\n' || c >= 32)) { + enterCharacter(c, shift); + } + } + io.InputQueueCharacters.resize(0); + } + } + } + + void TextEditor::handleMouseInputs() { + ImGuiIO &io = ImGui::GetIO(); + auto shift = io.KeyShift; + auto ctrl = io.ConfigMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl; + auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; + + if (ImGui::IsWindowHovered()) { + if (!alt) { + auto click = ImGui::IsMouseClicked(0); + auto doubleClick = ImGui::IsMouseDoubleClicked(0); + auto rightClick = ImGui::IsMouseClicked(1); + auto t = ImGui::GetTime(); + auto tripleClick = click && !doubleClick && (m_lastClick != -1.0f && (t - m_lastClick) < io.MouseDoubleClickTime); + bool resetBlinking = false; + /* + Left mouse button triple click + */ + + if (tripleClick) { + if (!ctrl) { + 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, getLineMaxColumn(line)); + } + + m_lastClick = -1.0f; + resetBlinking = true; + } + + /* + Left mouse button double click + */ + + else if (doubleClick) { + if (!ctrl) { + m_state.m_cursorPosition = screenPosToCoordinates(ImGui::GetMousePos()); + m_state.m_selection.m_start = findWordStart(m_state.m_cursorPosition); + m_state.m_selection.m_end = findWordEnd(m_state.m_cursorPosition); + } + + m_lastClick = (float) ImGui::GetTime(); + resetBlinking = true; + } + + /* + Left mouse button click + */ + else if (click) { + if (ctrl) { + m_state.m_cursorPosition = m_interactiveSelection.m_start = m_interactiveSelection.m_end = screenPosToCoordinates(ImGui::GetMousePos()); + selectWordUnderCursor(); + } else if (shift) { + m_interactiveSelection.m_end = screenPosToCoordinates(ImGui::GetMousePos()); + m_state.m_cursorPosition = m_interactiveSelection.m_end; + setSelection(m_interactiveSelection); + } else { + m_state.m_cursorPosition = m_interactiveSelection.m_start = m_interactiveSelection.m_end = screenPosToCoordinates(ImGui::GetMousePos()); + setSelection(m_interactiveSelection); + } + resetCursorBlinkTime(); + + ensureCursorVisible(); + m_lastClick = (float) ImGui::GetTime(); + } else if (rightClick) { + auto cursorPosition = screenPosToCoordinates(ImGui::GetMousePos()); + + if (!hasSelection() || m_state.m_selection.m_start > cursorPosition || cursorPosition > m_state.m_selection.m_end) { + m_state.m_cursorPosition = m_interactiveSelection.m_start = m_interactiveSelection.m_end = cursorPosition; + setSelection(m_interactiveSelection); + } + resetCursorBlinkTime(); + m_raiseContextMenu = true; + ImGui::SetWindowFocus(); + } + // Mouse left button dragging (=> update selection) + else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) { + io.WantCaptureMouse = true; + m_state.m_cursorPosition = m_interactiveSelection.m_end = screenPosToCoordinates(ImGui::GetMousePos()); + setSelection(m_interactiveSelection); + resetBlinking = true; + } + if (resetBlinking) + resetCursorBlinkTime(); + } + } + } + + inline void TextUnformattedColoredAt(const ImVec2 &pos, const ImU32 &color, const char *text) { + ImGui::SetCursorScreenPos(pos); + ImGui::PushStyleColor(ImGuiCol_Text, color); + ImGui::TextUnformatted(text); + ImGui::PopStyleColor(); + } + + uint32_t TextEditor::skipSpaces(const Coordinates &from) { + auto line = from.m_line; + if (line >= m_lines.size()) + return 0; + auto &lines = m_lines[line].m_chars; + auto &colors = m_lines[line].m_colors; + auto charIndex = lineCoordinateToIndex(from); + uint32_t s = 0; + while (charIndex < (int32_t) lines.size() && lines[charIndex] == ' ' && colors[charIndex] == 0x00) { + ++s; + ++charIndex; + } + if (m_updateFocus) + setFocus(); + return s; + } + + void TextEditor::setFocus() { + m_state.m_cursorPosition = m_focusAtCoords; + resetCursorBlinkTime(); + ensureCursorVisible(); + if (!this->m_readOnly) + ImGui::SetKeyboardFocusHere(0); + m_updateFocus = false; + } + + bool TextEditor::MatchedBracket::checkPosition(TextEditor *editor, const Coordinates &from) { + auto lineIndex = from.m_line; + auto line = editor->m_lines[lineIndex].m_chars; + auto colors = editor->m_lines[lineIndex].m_colors; + if (!line.empty() && colors.empty()) + return false; + auto result = editor->lineCoordinateToIndex(from); + auto character = line[result]; + auto color = colors[result]; + if (s_separators.find(character) != std::string::npos && (static_cast(color) == PaletteIndex::Separator || static_cast(color) == PaletteIndex::WarningText) || + s_operators.find(character) != std::string::npos && (static_cast(color) == PaletteIndex::Operator || static_cast(color) == PaletteIndex::WarningText)) { + if (m_nearCursor != editor->getCharacterCoordinates(lineIndex, result)) { + m_nearCursor = editor->getCharacterCoordinates(lineIndex, result); + m_changed = true; + } + m_active = true; + return true; + } + return false; + } + + int32_t TextEditor::MatchedBracket::detectDirection(TextEditor *editor, const Coordinates &from) { + std::string brackets = "()[]{}<>"; + int32_t result = -2; // dont check either + auto start = editor->setCoordinates(from); + if (start == TextEditor::Invalid) + return result; + auto lineIndex = start.m_line; + auto line = editor->m_lines[lineIndex].m_chars; + auto charIndex = editor->lineCoordinateToIndex(start); + auto ch2 = line[charIndex]; + auto idx2 = brackets.find(ch2); + if (charIndex == 0) {// no previous character + if (idx2 == std::string::npos) // no brackets + return -2;// dont check either + else + return 1; // check only current + }// charIndex > 0 + auto ch1 = line[charIndex - 1]; + auto idx1 = brackets.find(ch1); + if (idx1 == std::string::npos && idx2 == std::string::npos) // no brackets + return -2; + if (idx1 != std::string::npos && idx2 != std::string::npos) { + if (idx1 % 2) // closing bracket + any bracket + return -1; // check both and previous first + else if (!(idx1 % 2) && !(idx2 % 2)) // opening bracket + opening bracket + return 0; // check both and current first + } else if (idx1 != std::string::npos) // only first bracket + return 1; // check only previous + else if (idx2 != std::string::npos) // only second bracket return 2; // check only current - return result; -} + return result; + } -bool TextEditor::MatchedBracket::isNearABracket(TextEditor *editor, const Coordinates &from) { - if (editor->isEmpty()) - return false; - auto start = editor->setCoordinates(from); - if (start == TextEditor::Invalid) - return false; - auto lineIndex = start.m_line; - auto charIndex = editor->lineCoordinateToIndex(start); - auto direction1 = detectDirection(editor, start); - auto charCoords = editor->getCharacterCoordinates(lineIndex,charIndex); - int32_t direction2 = 1; - if (direction1 == -1 || direction1 == 1) { - if (checkPosition(editor, editor->setCoordinates(charCoords.m_line, charCoords.m_column - 1))) - return true; - if (direction1 == -1) - direction2 = 0; - } else if (direction1 == 2 || direction1 == 0) { - if (checkPosition(editor, charCoords)) - return true; - if (direction1 == 0) - direction2 = -1; - } - if (direction2 != 1) { - if (checkPosition(editor, editor->setCoordinates(charCoords.m_line, charCoords.m_column + direction2))) - return true; - } - uint64_t result = 0; - auto strLine = editor->m_lines[lineIndex].m_chars; - if (charIndex==0) { - if (strLine[0] == ' ') { - result = std::string::npos; - } else { - result = 0; + bool TextEditor::MatchedBracket::isNearABracket(TextEditor *editor, const Coordinates &from) { + if (editor->isEmpty()) + return false; + auto start = editor->setCoordinates(from); + if (start == TextEditor::Invalid) + return false; + auto lineIndex = start.m_line; + auto charIndex = editor->lineCoordinateToIndex(start); + auto direction1 = detectDirection(editor, start); + auto charCoords = editor->getCharacterCoordinates(lineIndex, charIndex); + int32_t direction2 = 1; + if (direction1 == -1 || direction1 == 1) { + if (checkPosition(editor, editor->setCoordinates(charCoords.m_line, charCoords.m_column - 1))) + return true; + if (direction1 == -1) + direction2 = 0; + } else if (direction1 == 2 || direction1 == 0) { + if (checkPosition(editor, charCoords)) + return true; + if (direction1 == 0) + direction2 = -1; } - } else { - result = strLine.find_last_not_of(' ', charIndex - 1); - } - if (result != std::string::npos) { - auto resultCoords = editor->getCharacterCoordinates(lineIndex,result); - if (checkPosition(editor, resultCoords)) - return true; - } - result = strLine.find_first_not_of(' ', charIndex); - if (result != std::string::npos) { - auto resultCoords = editor->getCharacterCoordinates(lineIndex,result); - if (checkPosition(editor, resultCoords)) - return true; - } - if (isActive()) { - editor->m_lines[m_nearCursor.m_line].m_colorized = false; - editor->m_lines[m_matched.m_line].m_colorized = false; - m_active = false; - editor->colorize(); - } - return false; -} - -void TextEditor::MatchedBracket::findMatchingBracket(TextEditor *editor) { - auto from = editor->setCoordinates(m_nearCursor); - if (from == TextEditor::Invalid) { - m_active = false; - return; - } - m_matched = from; - auto lineIndex = from.m_line; - auto maxLineIndex = editor->m_lines.size() - 1; - auto charIndex = editor->lineCoordinateToIndex(from); - std::string line = editor->m_lines[lineIndex].m_chars; - std::string colors = editor->m_lines[lineIndex].m_colors; - if (!line.empty() && colors.empty()) { - m_active = false; - return; - } - std::string brackets = "()[]{}<>"; - char bracketChar = line[charIndex]; - char color1; - auto idx = brackets.find_first_of(bracketChar); - if (idx == std::string::npos) { - if (m_active) { + if (direction2 != 1) { + if (checkPosition(editor, editor->setCoordinates(charCoords.m_line, charCoords.m_column + direction2))) + return true; + } + uint64_t result = 0; + auto strLine = editor->m_lines[lineIndex].m_chars; + if (charIndex == 0) { + if (strLine[0] == ' ') { + result = std::string::npos; + } else { + result = 0; + } + } else { + result = strLine.find_last_not_of(' ', charIndex - 1); + } + if (result != std::string::npos) { + auto resultCoords = editor->getCharacterCoordinates(lineIndex, result); + if (checkPosition(editor, resultCoords)) + return true; + } + result = strLine.find_first_not_of(' ', charIndex); + if (result != std::string::npos) { + auto resultCoords = editor->getCharacterCoordinates(lineIndex, result); + if (checkPosition(editor, resultCoords)) + return true; + } + if (isActive()) { + editor->m_lines[m_nearCursor.m_line].m_colorized = false; + editor->m_lines[m_matched.m_line].m_colorized = false; m_active = false; editor->colorize(); } - return; + return false; } - auto bracketChar2 = brackets[idx ^ 1]; - brackets = bracketChar; - brackets += bracketChar2; - int32_t direction = 1 - 2 * (idx % 2); - if (idx > 5) - color1 = static_cast(PaletteIndex::Operator); - else - color1 = static_cast(PaletteIndex::Separator); - char color = static_cast(PaletteIndex::WarningText); - int32_t depth = 1; - if (charIndex == (line.size()-1) * (1 + direction) / 2 ) { - if (lineIndex == maxLineIndex * (1 + direction) / 2) { + + void TextEditor::MatchedBracket::findMatchingBracket(TextEditor *editor) { + auto from = editor->setCoordinates(m_nearCursor); + if (from == TextEditor::Invalid) { m_active = false; return; } - lineIndex += direction; - line = editor->m_lines[lineIndex].m_chars; - colors = editor->m_lines[lineIndex].m_colors; + m_matched = from; + auto lineIndex = from.m_line; + auto maxLineIndex = editor->m_lines.size() - 1; + auto charIndex = editor->lineCoordinateToIndex(from); + std::string line = editor->m_lines[lineIndex].m_chars; + std::string colors = editor->m_lines[lineIndex].m_colors; if (!line.empty() && colors.empty()) { m_active = false; return; } - charIndex = (line.size()-1) * (1 - direction) / 2 - direction; - } - for (int32_t i = charIndex + direction; ; i += direction) { - if (direction == 1) - idx = line.find_first_of(brackets, i); + std::string brackets = "()[]{}<>"; + char bracketChar = line[charIndex]; + char color1; + auto idx = brackets.find_first_of(bracketChar); + if (idx == std::string::npos) { + if (m_active) { + m_active = false; + editor->colorize(); + } + return; + } + auto bracketChar2 = brackets[idx ^ 1]; + brackets = bracketChar; + brackets += bracketChar2; + int32_t direction = 1 - 2 * (idx % 2); + if (idx > 5) + color1 = static_cast(PaletteIndex::Operator); else - idx = line.find_last_of(brackets, i); - if (idx != std::string::npos) { - if (line[idx] == bracketChar && (colors[idx] == color || colors[idx] == color1)) { - ++depth; - i = idx; - } else if (line[idx] == bracketChar2 && (colors[idx] == color) || colors[idx] == color1) { - --depth; - if (depth == 0) { - if (m_matched != editor->getCharacterCoordinates(lineIndex, idx)) { - m_matched = editor->getCharacterCoordinates(lineIndex, idx); + color1 = static_cast(PaletteIndex::Separator); + char color = static_cast(PaletteIndex::WarningText); + int32_t depth = 1; + if (charIndex == (line.size() - 1) * (1 + direction) / 2) { + if (lineIndex == maxLineIndex * (1 + direction) / 2) { + m_active = false; + return; + } + lineIndex += direction; + line = editor->m_lines[lineIndex].m_chars; + colors = editor->m_lines[lineIndex].m_colors; + if (!line.empty() && colors.empty()) { + m_active = false; + return; + } + charIndex = (line.size() - 1) * (1 - direction) / 2 - direction; + } + for (int32_t i = charIndex + direction;; i += direction) { + if (direction == 1) + idx = line.find_first_of(brackets, i); + else + idx = line.find_last_of(brackets, i); + if (idx != std::string::npos) { + if (line[idx] == bracketChar && (colors[idx] == color || colors[idx] == color1)) { + ++depth; + i = idx; + } else if (line[idx] == bracketChar2 && (colors[idx] == color) || colors[idx] == color1) { + --depth; + if (depth == 0) { + if (m_matched != editor->getCharacterCoordinates(lineIndex, idx)) { + m_matched = editor->getCharacterCoordinates(lineIndex, idx); + m_changed = true; + } + m_active = true; + break; + } + i = idx; + } else { + i = idx; + } + } else { + if (direction == 1) + i = line.size() - 1; + else + i = 0; + } + if ((int32_t) (direction * i) >= (int32_t) ((line.size() - 1) * (1 + direction) / 2)) { + if (lineIndex == maxLineIndex * (1 + direction) / 2) { + if (m_active) { + m_active = false; m_changed = true; } - m_active = true; break; + } else { + lineIndex += direction; + line = editor->m_lines[lineIndex].m_chars; + colors = editor->m_lines[lineIndex].m_colors; + if (!line.empty() && colors.empty()) { + m_active = false; + return; + } + i = (line.size() - 1) * (1 - direction) / 2 - direction; } - i = idx; - } else { - i = idx; } - } else { - if (direction == 1) - i = line.size()-1; - else - i = 0; } - if ((int32_t)(direction * i) >= (int32_t)((line.size() - 1) * (1 + direction) / 2)) { - if (lineIndex == maxLineIndex * (1 + direction) / 2) { - if (m_active) { - m_active = false; - m_changed = true; - } - break; - } else { - lineIndex += direction; - line = editor->m_lines[lineIndex].m_chars; - colors = editor->m_lines[lineIndex].m_colors; - if (!line.empty() && colors.empty()) { - m_active = false; - return; - } - i = (line.size() - 1) * (1 - direction) / 2 - direction; - } + + if (hasChanged()) { + editor->m_lines[m_nearCursor.m_line].m_colorized = false; + editor->m_lines[m_matched.m_line].m_colorized = false; + + editor->colorize(); + m_changed = false; } } - if (hasChanged()) { - editor->m_lines[m_nearCursor.m_line].m_colorized = false; - editor->m_lines[m_matched.m_line].m_colorized = false; + void TextEditor::preRender() { + m_charAdvance = calculateCharAdvance(); + + /* Update palette with the current alpha from style */ + for (int32_t i = 0; i < (int32_t) PaletteIndex::Max; ++i) { + auto color = ImGui::ColorConvertU32ToFloat4(s_paletteBase[i]); + color.w *= ImGui::GetStyle().Alpha; + m_palette[i] = ImGui::ColorConvertFloat4ToU32(color); + } + + m_numberOfLinesDisplayed = getPageSize(); + + if (m_scrollToTop) { + m_scrollToTop = false; + ImGui::SetScrollY(0.f); + } + + if (m_scrollToBottom && ImGui::GetScrollMaxY() >= ImGui::GetScrollY()) { + m_scrollToBottom = false; + ImGui::SetScrollY(ImGui::GetScrollMaxY()); + } - editor->colorize(); - m_changed = false; } -} + 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)); + auto drawList = ImGui::GetWindowDrawList(); -void TextEditor::renderText(const char *title, const ImVec2 &lineNumbersStartPos, const ImVec2 &textEditorSize) { - /* Compute mCharAdvance regarding scaled font size (Ctrl + mouse wheel)*/ - const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; - m_charAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * m_lineSpacing); + if (m_state.m_selection.m_start <= lineCoords.m_end && m_state.m_selection.m_end > lineCoords.m_start) { + float selectionStart = textDistanceToLineStart(std::max(m_state.m_selection.m_start, lineCoords.m_start)); + float selectionEnd = textDistanceToLineStart(std::min(m_state.m_selection.m_end, lineCoords.m_end)) + m_charAdvance.x * (m_state.m_selection.m_end.m_line > lineNo); - /* Update palette with the current alpha from style */ - for (int32_t i = 0; i < (int32_t)PaletteIndex::Max; ++i) { - auto color = ImGui::ColorConvertU32ToFloat4(s_paletteBase[i]); - color.w *= ImGui::GetStyle().Alpha; - m_palette[i] = ImGui::ColorConvertFloat4ToU32(color); - } - - IM_ASSERT(m_lineBuffer.empty()); - - auto contentSize = textEditorSize; - auto drawList = ImGui::GetWindowDrawList(); - m_numberOfLinesDisplayed = getPageSize(); - - if (m_scrollToTop) { - m_scrollToTop = false; - ImGui::SetScrollY(0.f); - } - - if (m_scrollToBottom && ImGui::GetScrollMaxY() >= ImGui::GetScrollY()) { - m_scrollToBottom = false; - ImGui::SetScrollY(ImGui::GetScrollMaxY()); - } - - ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); - ImVec2 position = lineNumbersStartPos; - auto scrollX = ImGui::GetScrollX(); - if (m_setScrollY) - setScrollY(); - auto scrollY = ImGui::GetScrollY(); - if (m_setTopLine) - setTopLine(); - else - m_topLine = std::max(0.0F, (scrollY - m_topMargin) / m_charAdvance.y); - auto lineNo = m_topLine; - float globalLineMax = m_lines.size(); - auto lineMax = std::clamp(lineNo + m_numberOfLinesDisplayed, 0.0F, globalLineMax - 1.0F); - int32_t totalDigitCount = std::floor(std::log10(globalLineMax)) + 1; - - - if (!m_lines.empty()) { - - while (lineNo <= lineMax) { - ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x + m_leftMargin, m_topMargin + cursorScreenPos.y + std::floor(lineNo) * m_charAdvance.y); - ImVec2 textScreenPos = lineStartScreenPos; - - auto &line = m_lines[lineNo]; - auto colors = m_lines[lineNo].m_colors; - Coordinates lineStartCoord = setCoordinates(lineNo, 0); - Coordinates lineEndCoord = setCoordinates(lineNo, -1); - if (lineStartCoord == Invalid || lineEndCoord == Invalid) - return; - // Draw selection for the current line - float selectionStart = -1.0f; - float selectionEnd = -1.0f; - - if (m_state.m_selection.m_start <= lineEndCoord) - selectionStart = m_state.m_selection.m_start > lineStartCoord ? textDistanceToLineStart( - m_state.m_selection.m_start) : 0.0f; - if (m_state.m_selection.m_end > lineStartCoord) - selectionEnd = textDistanceToLineStart( - m_state.m_selection.m_end < lineEndCoord ? m_state.m_selection.m_end : lineEndCoord); - - if (m_state.m_selection.m_end.m_line > lineNo) - selectionEnd += m_charAdvance.x; - - if (selectionStart != -1 && selectionEnd != -1 && selectionStart < selectionEnd) { + if (selectionStart < selectionEnd) { ImVec2 rectStart(lineStartScreenPos.x + selectionStart, lineStartScreenPos.y); ImVec2 rectEnd(lineStartScreenPos.x + selectionEnd, lineStartScreenPos.y + m_charAdvance.y); - drawList->AddRectFilled(rectStart, rectEnd, m_palette[(int32_t)PaletteIndex::Selection]); + drawList->AddRectFilled(rectStart, rectEnd, m_palette[(int32_t) PaletteIndex::Selection]); } - ImVec2 lineNoStartScreenPos = ImVec2(position.x, m_topMargin + cursorScreenPos.y + std::floor(lineNo) * m_charAdvance.y); - auto start = ImVec2(lineNoStartScreenPos.x + m_lineNumberFieldWidth, lineStartScreenPos.y); - bool focused = ImGui::IsWindowFocused(); - if (!m_ignoreImGuiChild) - ImGui::EndChild(); - // Draw line number (right aligned) - if (m_showLineNumbers) { - ImGui::SetCursorScreenPos(position); - if (!m_ignoreImGuiChild) - ImGui::BeginChild("##lineNumbers"); - - int32_t padding = totalDigitCount - std::floor(std::log10(lineNo + 1)) - 1; - std::string space = std::string(padding,' '); - std::string lineNoStr = space + std::to_string((int32_t)(lineNo + 1)); - ImGui::SetCursorScreenPos(ImVec2(lineNumbersStartPos.x, lineStartScreenPos.y)); - if (ImGui::InvisibleButton(lineNoStr.c_str(),ImVec2(m_lineNumberFieldWidth, m_charAdvance.y))) { - if (m_breakpoints.contains(lineNo + 1)) - m_breakpoints.erase(lineNo + 1); - else - m_breakpoints.insert(lineNo + 1); - m_breakPointsChanged = true; - auto cursorPosition = setCoordinates(lineNo, 0); - if (cursorPosition == Invalid) - return; - - m_state.m_cursorPosition = cursorPosition; - - jumpToCoords(m_state.m_cursorPosition); - } - - // Draw breakpoints - if (m_breakpoints.count(lineNo + 1) != 0) { - auto end = ImVec2(lineNoStartScreenPos.x + contentSize.x + m_lineNumberFieldWidth, lineStartScreenPos.y + m_charAdvance.y); - drawList->AddRectFilled(ImVec2(lineNumbersStartPos.x, lineStartScreenPos.y), end, m_palette[(int32_t)PaletteIndex::Breakpoint]); - - drawList->AddCircleFilled(start + ImVec2(0, m_charAdvance.y) / 2, m_charAdvance.y / 3, m_palette[(int32_t)PaletteIndex::Breakpoint]); - drawList->AddCircle(start + ImVec2(0, m_charAdvance.y) / 2, m_charAdvance.y / 3, m_palette[(int32_t)PaletteIndex::Default]); - drawList->AddText(ImVec2(lineNoStartScreenPos.x + m_leftMargin, lineStartScreenPos.y), m_palette[(int32_t) PaletteIndex::LineNumber], lineNoStr.c_str()); - } - - if (m_state.m_cursorPosition.m_line == lineNo && m_showCursor) { - - // Highlight the current line (where the cursor is) - if (!hasSelection()) { - auto end = ImVec2(lineNoStartScreenPos.x + contentSize.x + m_lineNumberFieldWidth, lineStartScreenPos.y + m_charAdvance.y); - drawList->AddRectFilled(ImVec2(lineNumbersStartPos.x, lineStartScreenPos.y), end, m_palette[(int32_t)(focused ? PaletteIndex::CurrentLineFill : PaletteIndex::CurrentLineFillInactive)]); - drawList->AddRect(ImVec2(lineNumbersStartPos.x, lineStartScreenPos.y), end, m_palette[(int32_t)PaletteIndex::CurrentLineEdge], 1.0f); - } - } - - TextUnformattedColoredAt(ImVec2(m_leftMargin + lineNoStartScreenPos.x, lineStartScreenPos.y), m_palette[(int32_t) PaletteIndex::LineNumber], lineNoStr.c_str()); - } - if (m_showLineNumbers && !m_ignoreImGuiChild) - ImGui::EndChild(); - - if (!m_ignoreImGuiChild) - ImGui::BeginChild(title); - if (m_state.m_cursorPosition.m_line == lineNo && m_showCursor) { - // Render the cursor - if (focused) { - auto timeEnd = ImGui::GetTime() * 1000; - auto elapsed = timeEnd - m_startTime; - if (elapsed > s_cursorBlinkOnTime) { - float width = 1.0f; - auto charIndex = lineCoordinateToIndex(m_state.m_cursorPosition); - float cx = textDistanceToLineStart(m_state.m_cursorPosition); - - if (m_overwrite && charIndex < (int32_t)line.size()) { - auto c = std::string(line[charIndex]); - width = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, c.c_str()).x; - } - ImVec2 rectStart(lineStartScreenPos.x + cx, lineStartScreenPos.y); - ImVec2 rectEnd(lineStartScreenPos.x + cx + width, lineStartScreenPos.y + m_charAdvance.y); - drawList->AddRectFilled(rectStart, rectEnd, m_palette[(int32_t)PaletteIndex::Cursor]); - if (elapsed > s_cursorBlinkInterval) - m_startTime = timeEnd; - if (m_matchedBracket.isNearABracket(this, m_state.m_cursorPosition)) - m_matchedBracket.findMatchingBracket(this); - } - } - } - - // Render goto buttons - auto lineText = getLineText(lineNo); - Coordinates gotoKey = setCoordinates(lineNo + 1, 0); - if (gotoKey != Invalid) { - std::string errorLineColumn; - bool found = false; - for (auto text: m_clickableText) { - if (lineText.find(text) == 0) { - errorLineColumn = lineText.substr(text.size()); - if (!errorLineColumn.empty()) { - found = true; - break; - } - } - } - if (found) { - int32_t currLine = 0, currColumn = 0; - if (auto idx = errorLineColumn.find(":"); idx != std::string::npos) { - auto errorLine = errorLineColumn.substr(0, idx); - if (!errorLine.empty()) - currLine = std::stoi(errorLine) - 1; - auto errorColumn = errorLineColumn.substr(idx + 1); - if (!errorColumn.empty()) - currColumn = std::stoi(errorColumn) - 1; - } - 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()); - m_errorGotoBoxes[gotoKey] = box; - CursorChangeBox cursorBox = CursorChangeBox(ImRect({errorStart, errorEnd})); - m_cursorBoxes[gotoKey] = cursorBox; - } - } - } - if (m_cursorBoxes.find(gotoKey) != m_cursorBoxes.end()) { - auto box = m_cursorBoxes[gotoKey]; - if (box.trigger()) box.callback(); - } - - if (m_errorGotoBoxes.find(gotoKey) != m_errorGotoBoxes.end()) { - auto box = m_errorGotoBoxes[gotoKey]; - if (box.trigger()) box.callback(); - } - } - // Render colorized text - if (line.empty()) { - ImGui::Dummy(m_charAdvance); - lineNo = std::floor(lineNo + 1.0F); - if (m_updateFocus) - setFocus(); - continue; - } - int32_t i = 0; - auto colorsSize = static_cast(colors.size()); - auto spacesToSkip = setCoordinates(lineNo, i); - if (spacesToSkip == Invalid) - continue; - i += skipSpaces(spacesToSkip); - while (i < colorsSize) { - char color = colors[i]; - uint32_t tokenLength = colors.find_first_not_of(color, i) - i; - if (m_updateFocus) - setFocus(); - color = std::clamp(color, (char)PaletteIndex::Default, (char)((uint8_t)PaletteIndex::Max-1)); - tokenLength = std::clamp(tokenLength, 1u, colorsSize - i); - bool underwaved = false; - ErrorMarkers::iterator errorIt; - auto errorMarkerCoords = setCoordinates(lineNo, i); - if (errorMarkerCoords == Invalid) - continue; - errorMarkerCoords.m_line += 1; - errorMarkerCoords.m_column += 1; - if (errorIt = m_errorMarkers.find(errorMarkerCoords); errorIt != m_errorMarkers.end()) { - underwaved = true; - } - - m_lineBuffer = line.substr(i, tokenLength); - ImGui::PushStyleColor(ImGuiCol_Text, m_palette[(uint64_t) color]); - auto charsBefore = ImGui::CalcTextSize(line.m_chars.substr(0, i).c_str()).x; - const ImVec2 textScreenPosition(lineStartScreenPos.x + charsBefore, lineStartScreenPos.y); - ImGui::SetCursorScreenPos(textScreenPosition); - ImGui::TextUnformatted(m_lineBuffer.c_str()); - ImGui::PopStyleColor(); - m_lineBuffer.clear(); - if (underwaved) { - auto lineStart = setCoordinates(lineNo, i); - if (lineStart == Invalid) - continue; - auto textStart = textDistanceToLineStart(lineStart); - auto begin = ImVec2(lineStartScreenPos.x + textStart, lineStartScreenPos.y); - auto errorLength = errorIt->second.first; - auto errorMessage = errorIt->second.second; - if (errorLength == 0) - errorLength = line.size() - i - 1; - auto end = underwaves(begin, errorLength, m_palette[(int32_t) PaletteIndex::ErrorMarker]); - auto keyCoords = setCoordinates(lineNo, i); - if (keyCoords == Invalid) - continue; - Coordinates key = keyCoords + Coordinates(1, 1); - ErrorHoverBox box = ErrorHoverBox(ImRect({begin, end}), key, errorMessage.c_str()); - m_errorHoverBoxes[key] = box; - } - auto keyCoords = setCoordinates(lineNo , i); - if (keyCoords == Invalid) - continue; - Coordinates key = keyCoords + Coordinates(1,1); - if (m_errorHoverBoxes.find(key) != m_errorHoverBoxes.end()) { - auto box = m_errorHoverBoxes[key]; - if (box.trigger()) box.callback(); - } - - - i += tokenLength; - auto nextSpacesToSkip = setCoordinates(lineNo, i); - if (nextSpacesToSkip == Invalid) - continue; - i += skipSpaces(nextSpacesToSkip); - - } - - lineNo = std::floor(lineNo + 1.0F); } } - ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x + m_leftMargin, m_topMargin + cursorScreenPos.y + std::floor(lineNo) * m_charAdvance.y); - if (!m_ignoreImGuiChild) - ImGui::EndChild(); - if (m_showLineNumbers && !m_ignoreImGuiChild) { - ImGui::BeginChild("##lineNumbers"); - ImGui::SetCursorScreenPos(ImVec2(lineNumbersStartPos.x, lineStartScreenPos.y)); - ImGui::Dummy(ImVec2(m_lineNumberFieldWidth, (globalLineMax - lineMax - 1) * m_charAdvance.y + ImGui::GetCurrentWindow()->InnerClipRect.GetHeight() - m_charAdvance.y)); - ImGui::EndChild(); - } - if (!m_ignoreImGuiChild) - ImGui::BeginChild(title); - - ImGui::SetCursorScreenPos(lineStartScreenPos); - if (m_showLineNumbers) - ImGui::Dummy(ImVec2(m_longestLineLength * m_charAdvance.x + m_charAdvance.x, (globalLineMax - lineMax - 2.0F) * m_charAdvance.y + ImGui::GetCurrentWindow()->InnerClipRect.GetHeight())); - else - ImGui::Dummy(ImVec2(m_longestLineLength * m_charAdvance.x + m_charAdvance.x, (globalLineMax - lineMax - 3.0F) * m_charAdvance.y + ImGui::GetCurrentWindow()->InnerClipRect.GetHeight() - 1.0f)); - - if (m_scrollToCursor) - ensureCursorVisible(); - - - if (m_topMarginChanged) { - m_topMarginChanged = false; - auto window = ImGui::GetCurrentWindow(); - auto maxScroll = window->ScrollMax.y; - if (maxScroll > 0) { - float pixelCount; - if (m_newTopMargin > m_topMargin) { - pixelCount = m_newTopMargin - m_topMargin; - } else if (m_newTopMargin > 0) { - pixelCount = m_topMargin - m_newTopMargin; - } else { - pixelCount = m_topMargin; - } - auto oldScrollY = ImGui::GetScrollY(); - - if (m_newTopMargin > m_topMargin) - m_shiftedScrollY = oldScrollY + pixelCount; - else - m_shiftedScrollY = oldScrollY - pixelCount; - ImGui::SetScrollY(m_shiftedScrollY); - m_topMargin = m_newTopMargin; - } - } -} - -void TextEditor::render(const char *title, const ImVec2 &size, bool border) { - m_withinRender = true; - - if (m_lines.capacity() < 2 * m_lines.size()) - m_lines.reserve(2 * m_lines.size()); - - auto scrollBg = ImGui::GetStyleColorVec4(ImGuiCol_ScrollbarBg); - scrollBg.w = 0.0f; - auto scrollBarSize = ImGui::GetStyle().ScrollbarSize; - ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(m_palette[(int32_t) PaletteIndex::Background])); - ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGui::ColorConvertFloat4ToU32(scrollBg)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarRounding,0); - ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize,scrollBarSize); - - auto position = ImGui::GetCursorScreenPos(); - if (m_showLineNumbers ) { - std::string lineNumber = " " + std::to_string(m_lines.size()) + " "; - m_lineNumberFieldWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, lineNumber.c_str(), nullptr, nullptr).x + m_leftMargin; - ImGui::SetNextWindowPos(position); + void TextEditor::drawLineNumbers(ImVec2 position, float lineNo, const ImVec2 &contentSize, bool focused, ImDrawList *drawList) { + ImVec2 lineStartScreenPos = s_cursorScreenPosition + ImVec2(m_leftMargin, m_topMargin + std::floor(lineNo) * m_charAdvance.y); + ImVec2 lineNoStartScreenPos = ImVec2(position.x, m_topMargin + s_cursorScreenPosition.y + std::floor(lineNo) * m_charAdvance.y); + auto start = ImVec2(lineNoStartScreenPos.x + m_lineNumberFieldWidth, lineStartScreenPos.y); + int32_t totalDigitCount = std::floor(std::log10(m_lines.size())) + 1; ImGui::SetCursorScreenPos(position); - auto lineNoSize = ImVec2(m_lineNumberFieldWidth, size.y); - if (!m_ignoreImGuiChild) { - ImGui::BeginChild("##lineNumbers", lineNoSize, false, ImGuiWindowFlags_NoScrollbar); + if (!m_ignoreImGuiChild) + ImGui::BeginChild("##lineNumbers"); + + int32_t padding = totalDigitCount - std::floor(std::log10(lineNo + 1)) - 1; + std::string space = std::string(padding, ' '); + std::string lineNoStr = space + std::to_string((int32_t) (lineNo + 1)); + ImGui::SetCursorScreenPos(ImVec2(position.x, lineStartScreenPos.y)); + if (ImGui::InvisibleButton(lineNoStr.c_str(), ImVec2(m_lineNumberFieldWidth, m_charAdvance.y))) { + if (m_breakpoints.contains(lineNo + 1)) + m_breakpoints.erase(lineNo + 1); + else + m_breakpoints.insert(lineNo + 1); + m_breakPointsChanged = true; + auto cursorPosition = setCoordinates(lineNo, 0); + if (cursorPosition == Invalid) + return; + + m_state.m_cursorPosition = cursorPosition; + + jumpToCoords(m_state.m_cursorPosition); + } + // Draw breakpoints + if (m_breakpoints.count(lineNo + 1) != 0) { + auto end = ImVec2(lineNoStartScreenPos.x + contentSize.x + m_lineNumberFieldWidth, lineStartScreenPos.y + m_charAdvance.y); + drawList->AddRectFilled(ImVec2(position.x, lineStartScreenPos.y), end, m_palette[(int32_t) PaletteIndex::Breakpoint]); + + drawList->AddCircleFilled(start + ImVec2(0, m_charAdvance.y) / 2, m_charAdvance.y / 3, m_palette[(int32_t) PaletteIndex::Breakpoint]); + drawList->AddCircle(start + ImVec2(0, m_charAdvance.y) / 2, m_charAdvance.y / 3, m_palette[(int32_t) PaletteIndex::Default]); + drawList->AddText(ImVec2(lineNoStartScreenPos.x + m_leftMargin, lineStartScreenPos.y), m_palette[(int32_t) PaletteIndex::LineNumber], lineNoStr.c_str()); + } + + if (m_state.m_cursorPosition.m_line == lineNo && m_showCursor) { + + // Highlight the current line (where the cursor is) + if (!hasSelection()) { + auto end = ImVec2(lineNoStartScreenPos.x + contentSize.x + m_lineNumberFieldWidth, lineStartScreenPos.y + m_charAdvance.y); + drawList->AddRectFilled(ImVec2(position.x, lineStartScreenPos.y), end, m_palette[(int32_t) (focused ? PaletteIndex::CurrentLineFill : PaletteIndex::CurrentLineFillInactive)]); + drawList->AddRect(ImVec2(position.x, lineStartScreenPos.y), end, m_palette[(int32_t) PaletteIndex::CurrentLineEdge], 1.0f); + } + } + + TextUnformattedColoredAt(ImVec2(m_leftMargin + lineNoStartScreenPos.x, lineStartScreenPos.y), m_palette[(int32_t) PaletteIndex::LineNumber], lineNoStr.c_str()); + + if (!m_ignoreImGuiChild) + ImGui::EndChild(); + } + + void TextEditor::renderCursor(float lineNo, ImDrawList *drawList) { + ImVec2 lineStartScreenPos = s_cursorScreenPosition + ImVec2(m_leftMargin, m_topMargin + std::floor(lineNo) * m_charAdvance.y); + auto timeEnd = ImGui::GetTime() * 1000; + auto elapsed = timeEnd - m_startTime; + if (elapsed > s_cursorBlinkOnTime) { + float width = 1.0f; + uint64_t charIndex = lineCoordinateToIndex(m_state.m_cursorPosition); + float cx = textDistanceToLineStart(m_state.m_cursorPosition); + auto &line = m_lines[std::floor(lineNo)]; + if (m_overwrite && charIndex < (int32_t) line.size()) { + char c = line[charIndex]; + std::string s(1, c); + width = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, s.c_str()).x; + } + ImVec2 rectStart(lineStartScreenPos.x + cx, lineStartScreenPos.y); + ImVec2 rectEnd(lineStartScreenPos.x + cx + width, lineStartScreenPos.y + m_charAdvance.y); + drawList->AddRectFilled(rectStart, rectEnd, m_palette[(int32_t) PaletteIndex::Cursor]); + if (elapsed > s_cursorBlinkInterval) + m_startTime = timeEnd; + if (m_matchedBracket.isNearABracket(this, m_state.m_cursorPosition)) + m_matchedBracket.findMatchingBracket(this); + } + } + + void TextEditor::renderGotoButtons(float lineNo) { + 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); + if (gotoKey != Invalid) { + std::string errorLineColumn; + bool found = false; + for (auto text: m_clickableText) { + if (lineText.find(text) == 0) { + errorLineColumn = lineText.substr(text.size()); + if (!errorLineColumn.empty()) { + found = true; + break; + } + } + } + if (found) { + int32_t currLine = 0, currColumn = 0; + if (auto idx = errorLineColumn.find(":"); idx != std::string::npos) { + auto errorLine = errorLineColumn.substr(0, idx); + if (!errorLine.empty()) + currLine = std::stoi(errorLine) - 1; + auto errorColumn = errorLineColumn.substr(idx + 1); + if (!errorColumn.empty()) + currColumn = std::stoi(errorColumn) - 1; + } + 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()); + m_errorGotoBoxes[gotoKey] = box; + CursorChangeBox cursorBox = CursorChangeBox(ImRect({errorStart, errorEnd})); + m_cursorBoxes[gotoKey] = cursorBox; + } + } + } + if (m_cursorBoxes.find(gotoKey) != m_cursorBoxes.end()) { + auto box = m_cursorBoxes[gotoKey]; + if (box.trigger()) box.callback(); + } + + if (m_errorGotoBoxes.find(gotoKey) != m_errorGotoBoxes.end()) { + auto box = m_errorGotoBoxes[gotoKey]; + if (box.trigger()) box.callback(); + } + } + } + + void TextEditor::drawText(Coordinates &lineStart, uint64_t i, uint32_t tokenLength, char color) { + auto &line = m_lines[lineStart.m_line]; + ImVec2 lineStartScreenPos = s_cursorScreenPosition + ImVec2(m_leftMargin, m_topMargin + std::floor(lineStart.m_line) * m_charAdvance.y); + auto textStart = textDistanceToLineStart(lineStart); + auto begin = lineStartScreenPos + ImVec2(textStart, 0); + + TextUnformattedColoredAt(begin, m_palette[(int32_t) color], line.substr(i, tokenLength).c_str()); + + ErrorMarkers::iterator errorIt; + auto key = lineStart + Coordinates(1, 1); + if (errorIt = m_errorMarkers.find(key); errorIt != m_errorMarkers.end()) { + auto errorMessage = errorIt->second.second; + auto errorLength = errorIt->second.first; + if (errorLength == 0) + errorLength = line.size() - i - 1; + + auto end = underwaves(begin, errorLength, m_palette[(int32_t) PaletteIndex::ErrorMarker]); + ErrorHoverBox box = ErrorHoverBox(ImRect({begin, end}), key, errorMessage.c_str()); + m_errorHoverBoxes[key] = box; + } + if (m_errorHoverBoxes.find(key) != m_errorHoverBoxes.end()) { + auto box = m_errorHoverBoxes[key]; + if (box.trigger()) box.callback(); + } + lineStart = lineStart + Coordinates(0, tokenLength); + } + + void TextEditor::postRender(const char *title, ImVec2 position, float lineNo) { + ImVec2 lineStartScreenPos = ImVec2(s_cursorScreenPosition.x + m_leftMargin, m_topMargin + s_cursorScreenPosition.y + std::floor(lineNo) * m_charAdvance.y); + float globalLineMax = m_lines.size(); + auto lineMax = std::clamp(lineNo + m_numberOfLinesDisplayed, 0.0F, globalLineMax - 1.0F); + if (!m_ignoreImGuiChild) + ImGui::EndChild(); + + if (m_showLineNumbers && !m_ignoreImGuiChild) { + ImGui::BeginChild("##lineNumbers"); + ImGui::SetCursorScreenPos(ImVec2(position.x, lineStartScreenPos.y)); + ImGui::Dummy(ImVec2(m_lineNumberFieldWidth, (globalLineMax - lineMax - 1) * m_charAdvance.y + ImGui::GetCurrentWindow()->InnerClipRect.GetHeight() - m_charAdvance.y)); ImGui::EndChild(); } - } else { - m_lineNumberFieldWidth = 0; - } + if (!m_ignoreImGuiChild) + ImGui::BeginChild(title); - ImVec2 textEditorSize = size; - textEditorSize.x -= m_lineNumberFieldWidth; - - bool scroll_x = m_longestLineLength * m_charAdvance.x >= textEditorSize.x; - - bool scroll_y = m_lines.size() > 1; - if (!border) - textEditorSize.x -= scrollBarSize; - ImGui::SetCursorScreenPos(ImVec2(position.x + m_lineNumberFieldWidth, position.y)); - ImGuiChildFlags childFlags = border ? ImGuiChildFlags_Borders : ImGuiChildFlags_None; - ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove; - if (!m_ignoreImGuiChild) - ImGui::BeginChild(title, textEditorSize, childFlags, windowFlags); - auto window = ImGui::GetCurrentWindow(); - window->ScrollbarSizes = ImVec2(scrollBarSize * scroll_x, scrollBarSize * scroll_y); - ImGui::GetCurrentWindowRead()->ScrollbarSizes = ImVec2(scrollBarSize * scroll_y, scrollBarSize * scroll_x); - if (scroll_y) { - ImGui::GetCurrentWindow()->ScrollbarY = true; - ImGui::Scrollbar(ImGuiAxis_Y); - ImGui::GetCurrentWindow()->ScrollbarY = false; - } - if (scroll_x) { - ImGui::GetCurrentWindow()->ScrollbarX = true; - ImGui::Scrollbar(ImGuiAxis_X); - ImGui::GetCurrentWindow()->ScrollbarX = false; - } - - if (m_handleKeyboardInputs) { - handleKeyboardInputs(); - } - - if (m_handleMouseInputs) - handleMouseInputs(); + ImGui::SetCursorScreenPos(lineStartScreenPos); + if (m_showLineNumbers) + ImGui::Dummy(ImVec2(m_longestLineLength * m_charAdvance.x + m_charAdvance.x, (globalLineMax - lineMax - 2.0F) * m_charAdvance.y + ImGui::GetCurrentWindow()->InnerClipRect.GetHeight())); + else + ImGui::Dummy(ImVec2(m_longestLineLength * m_charAdvance.x + m_charAdvance.x, (globalLineMax - lineMax - 3.0F) * m_charAdvance.y + ImGui::GetCurrentWindow()->InnerClipRect.GetHeight() - 1.0f)); - colorizeInternal(); - renderText(title, position, textEditorSize); + if (m_topMarginChanged) { + m_topMarginChanged = false; + auto window = ImGui::GetCurrentWindow(); + auto maxScroll = window->ScrollMax.y; + if (maxScroll > 0) { + float pixelCount; + if (m_newTopMargin > m_topMargin) { + pixelCount = m_newTopMargin - m_topMargin; + } else if (m_newTopMargin > 0) { + pixelCount = m_topMargin - m_newTopMargin; + } else { + pixelCount = m_topMargin; + } + auto oldScrollY = ImGui::GetScrollY(); - if (!m_ignoreImGuiChild) - ImGui::EndChild(); - - ImGui::PopStyleVar(3); - ImGui::PopStyleColor(2); - - m_withinRender = false; - ImGui::SetCursorScreenPos(ImVec2(position.x,position.y+size.y-1)); - ImGui::Dummy({}); -} - -void TextEditor::setText(const std::string &text, bool undo) { - UndoRecord u; - 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) - return; - } - auto vectorString = splitString(text, "\n", false); - auto lineCount = vectorString.size(); - if (lineCount == 0 ) { - m_lines.resize(1); - m_lines[0].clear(); - } else { - m_lines.resize(lineCount); - uint64_t i = 0; - for (auto line : vectorString) { - m_lines[i].setLine(line); - m_lines[i].m_colorized = false; - i++; + if (m_newTopMargin > m_topMargin) + m_shiftedScrollY = oldScrollY + pixelCount; + else + m_shiftedScrollY = oldScrollY - pixelCount; + ImGui::SetScrollY(m_shiftedScrollY); + m_topMargin = m_newTopMargin; + } } } - 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) + + ImVec2 TextEditor::calculateCharAdvance() const { + /* Compute mCharAdvance regarding scaled font size (Ctrl + mouse wheel)*/ + const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; + return ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * m_lineSpacing); + } + + void TextEditor::renderText(const char *title, const ImVec2 &lineNumbersStartPos, const ImVec2 &textEditorSize) { + + preRender(); + auto drawList = ImGui::GetWindowDrawList(); + s_cursorScreenPosition = ImGui::GetCursorScreenPos(); + ImVec2 position = lineNumbersStartPos; + auto scrollX = ImGui::GetScrollX(); + if (m_setScrollY) + setScrollY(); + auto scrollY = ImGui::GetScrollY(); + if (m_setTopLine) + setTopLine(); + else + m_topLine = std::max(0.0F, (scrollY - m_topMargin) / m_charAdvance.y); + auto lineNo = m_topLine; + + auto lineMax = std::clamp(lineNo + m_numberOfLinesDisplayed, 0.0F, m_lines.size() - 1.0F); + + if (!m_lines.empty()) { + bool focused = ImGui::IsWindowFocused(); + + while (lineNo <= lineMax) { + + drawSelection(lineNo); + + if (!m_ignoreImGuiChild) + ImGui::EndChild(); + + if (m_showLineNumbers) + drawLineNumbers(position, lineNo, textEditorSize, focused, drawList); + + if (!m_ignoreImGuiChild) + ImGui::BeginChild(title); + + if (m_state.m_cursorPosition.m_line == lineNo && m_showCursor && focused) + renderCursor(lineNo, drawList); + + renderGotoButtons(lineNo); + + // Render colorized text + + auto &line = m_lines[lineNo]; + if (line.empty()) { + ImGui::Dummy(m_charAdvance); + lineNo = std::floor(lineNo + 1.0F); + if (m_updateFocus) + setFocus(); + continue; + } + auto colors = m_lines[lineNo].m_colors; + uint64_t colorsSize = colors.size(); + uint64_t i = skipSpaces(setCoordinates(lineNo, 0)); + while (i < colorsSize) { + char color = std::clamp(colors[i], (char) PaletteIndex::Default, (char) ((uint8_t) PaletteIndex::Max - 1)); + uint32_t tokenLength = std::clamp((uint64_t) (colors.find_first_not_of(color, i) - i),(uint64_t) 1, colorsSize - i); + if (m_updateFocus) + setFocus(); + auto lineStart = setCoordinates(lineNo, i); + + drawText(lineStart, i, tokenLength, color); + + i += (tokenLength + skipSpaces(lineStart)); + } + + lineNo = std::floor(lineNo + 1.0F); + } + } + + if (m_scrollToCursor) + ensureCursorVisible(); + + postRender(title, position, lineNo); + + } + + void TextEditor::render(const char *title, const ImVec2 &size, bool border) { + m_withinRender = true; + + if (m_lines.capacity() < 2 * m_lines.size()) + m_lines.reserve(2 * m_lines.size()); + + auto scrollBg = ImGui::GetStyleColorVec4(ImGuiCol_ScrollbarBg); + scrollBg.w = 0.0f; + auto scrollBarSize = ImGui::GetStyle().ScrollbarSize; + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(m_palette[(int32_t) PaletteIndex::Background])); + ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGui::ColorConvertFloat4ToU32(scrollBg)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarRounding, 0); + ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, scrollBarSize); + + auto position = ImGui::GetCursorScreenPos(); + if (m_showLineNumbers) { + std::string lineNumber = " " + std::to_string(m_lines.size()) + " "; + m_lineNumberFieldWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, lineNumber.c_str(), nullptr, nullptr).x + m_leftMargin; + ImGui::SetNextWindowPos(position); + ImGui::SetCursorScreenPos(position); + auto lineNoSize = ImVec2(m_lineNumberFieldWidth, size.y); + if (!m_ignoreImGuiChild) { + ImGui::BeginChild("##lineNumbers", lineNoSize, false, ImGuiWindowFlags_NoScrollbar); + ImGui::EndChild(); + } + } else { + m_lineNumberFieldWidth = 0; + } + + ImVec2 textEditorSize = size; + textEditorSize.x -= m_lineNumberFieldWidth; + + bool scroll_x = m_longestLineLength * m_charAdvance.x >= textEditorSize.x; + + bool scroll_y = m_lines.size() > 1; + if (!border) + textEditorSize.x -= scrollBarSize; + ImGui::SetCursorScreenPos(ImVec2(position.x + m_lineNumberFieldWidth, position.y)); + ImGuiChildFlags childFlags = border ? ImGuiChildFlags_Borders : ImGuiChildFlags_None; + ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove; + if (!m_ignoreImGuiChild) + ImGui::BeginChild(title, textEditorSize, childFlags, windowFlags); + auto window = ImGui::GetCurrentWindow(); + window->ScrollbarSizes = ImVec2(scrollBarSize * scroll_x, scrollBarSize * scroll_y); + ImGui::GetCurrentWindowRead()->ScrollbarSizes = ImVec2(scrollBarSize * scroll_y, scrollBarSize * scroll_x); + if (scroll_y) { + ImGui::GetCurrentWindow()->ScrollbarY = true; + ImGui::Scrollbar(ImGuiAxis_Y); + ImGui::GetCurrentWindow()->ScrollbarY = false; + } + if (scroll_x) { + ImGui::GetCurrentWindow()->ScrollbarX = true; + ImGui::Scrollbar(ImGuiAxis_X); + ImGui::GetCurrentWindow()->ScrollbarX = false; + } + + if (m_handleKeyboardInputs) { + handleKeyboardInputs(); + } + + if (m_handleMouseInputs) + handleMouseInputs(); + + + colorizeInternal(); + renderText(title, position, textEditorSize); + + if (!m_ignoreImGuiChild) + ImGui::EndChild(); + + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(2); + + m_withinRender = false; + ImGui::SetCursorScreenPos(ImVec2(position.x, position.y + size.y - 1)); + ImGui::Dummy({}); + } + + void TextEditor::setText(const std::string &text, bool undo) { + UndoRecord u; + 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) + return; + } + auto vectorString = splitString(text, "\n", false); + auto lineCount = vectorString.size(); + if (lineCount == 0) { + m_lines.resize(1); + m_lines[0].clear(); + } else { + m_lines.resize(lineCount); + uint64_t i = 0; + for (auto line: vectorString) { + m_lines[i].setLine(line); + m_lines[i].m_colorized = false; + i++; + } + } + 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) + return; + } + m_textChanged = true; + m_scrollToTop = true; + if (!m_readOnly && undo) { + u.m_after = m_state; + + addUndo(u); + } + + colorize(); + } + + void TextEditor::enterCharacter(ImWchar character, bool shift) { + if (m_readOnly) return; - } - m_textChanged = true; - m_scrollToTop = true; - if (!m_readOnly && undo) { - u.m_after = m_state; - addUndo(u); - } + UndoRecord u; - colorize(); -} + u.m_before = m_state; -void TextEditor::enterCharacter(ImWchar character, bool shift) { - if (m_readOnly) - return; + resetCursorBlinkTime(); - UndoRecord u; + if (hasSelection()) { + if (character == '\t') { - u.m_before = m_state; + auto start = m_state.m_selection.m_start; + auto end = m_state.m_selection.m_end; + auto originalEnd = end; - resetCursorBlinkTime(); + start.m_column = 0; - if (hasSelection()) { - if (character == '\t') { + if (end.m_column == 0 && end.m_line > 0) + --end.m_line; + if (end.m_line >= (int32_t) m_lines.size()) + end.m_line = isEmpty() ? 0 : (int32_t) m_lines.size() - 1; + end.m_column = getLineMaxColumn(end.m_line); - auto start = m_state.m_selection.m_start; - auto end = m_state.m_selection.m_end; - auto originalEnd = end; + u.m_removedSelection = Selection(start, end); + u.m_removed = getText(u.m_removedSelection); - start.m_column = 0; + bool modified = false; - if (end.m_column == 0 && end.m_line > 0) - --end.m_line; - if (end.m_line >= (int32_t)m_lines.size()) - end.m_line = isEmpty() ? 0 : (int32_t)m_lines.size() - 1; - end.m_column = getLineMaxColumn(end.m_line); - - u.m_removedSelection = Selection(start, end); - u.m_removed = getText(u.m_removedSelection); - - bool modified = false; - - for (int32_t i = start.m_line; i <= end.m_line; i++) { - auto &line = m_lines[i]; - if (shift) { - if (!line.empty()) { - auto index = line.m_chars.find_first_not_of(' ', 0); - if (index == std::string::npos) - index = line.size() - 1; - if (index == 0) continue; - uint64_t spacesToRemove = (index % m_tabSize) ? (index % m_tabSize) : m_tabSize; - spacesToRemove = std::min(spacesToRemove, line.size()); - line.erase(line.begin(), spacesToRemove); + for (int32_t i = start.m_line; i <= end.m_line; i++) { + auto &line = m_lines[i]; + if (shift) { + if (!line.empty()) { + auto index = line.m_chars.find_first_not_of(' ', 0); + if (index == std::string::npos) + index = line.size() - 1; + if (index == 0) continue; + uint64_t spacesToRemove = (index % m_tabSize) ? (index % m_tabSize) : m_tabSize; + spacesToRemove = std::min(spacesToRemove, line.size()); + line.erase(line.begin(), spacesToRemove); + line.m_colorized = false; + modified = true; + } + } else { + auto spacesToInsert = m_tabSize - (start.m_column % m_tabSize); + std::string spaces(spacesToInsert, ' '); + line.insert(line.begin(), spaces.begin(), spaces.end()); line.m_colorized = false; modified = true; } - } else { - auto spacesToInsert = m_tabSize - (start.m_column % m_tabSize); - std::string spaces(spacesToInsert, ' '); - line.insert(line.begin(), spaces.begin(), spaces.end()); - line.m_colorized = false; - modified = true; - } - } - - if (modified) { - Coordinates rangeEnd; - if (originalEnd.m_column != 0) { - end = setCoordinates(end.m_line, -1); - if (end == Invalid) - return; - rangeEnd = end; - u.m_added = getText(Selection(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_addedSelection = Selection( start, rangeEnd); - u.m_after = m_state; + if (modified) { + Coordinates rangeEnd; + if (originalEnd.m_column != 0) { + end = setCoordinates(end.m_line, -1); + if (end == Invalid) + return; + rangeEnd = end; + u.m_added = getText(Selection(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)); + } - m_state.m_selection = Selection( start, end); - addUndo(u); + u.m_addedSelection = Selection(start, rangeEnd); + u.m_after = m_state; - m_textChanged = true; + m_state.m_selection = Selection(start, end); + addUndo(u); - ensureCursorVisible(); + m_textChanged = true; + + ensureCursorVisible(); + } + + return; + } // c == '\t' + else { + u.m_removed = getSelectedText(); + u.m_removedSelection = Selection(m_state.m_selection); + deleteSelection(); } + } // HasSelection - return; - } // c == '\t' - else { - u.m_removed = getSelectedText(); - u.m_removedSelection = Selection( m_state.m_selection); - deleteSelection(); - } - } // HasSelection + auto coord = setCoordinates(m_state.m_cursorPosition); + u.m_addedSelection.m_start = coord; - auto coord = setCoordinates(m_state.m_cursorPosition); - u.m_addedSelection.m_start = coord; + if (m_lines.empty()) + m_lines.push_back(Line()); - if (m_lines.empty()) - m_lines.push_back(Line()); + if (character == '\n') { + insertLine(coord.m_line + 1); + auto &line = m_lines[coord.m_line]; + auto &newLine = m_lines[coord.m_line + 1]; - if (character == '\n') { - insertLine(coord.m_line + 1); - auto &line = m_lines[coord.m_line]; - auto &newLine = m_lines[coord.m_line + 1]; + if (m_languageDefinition.m_autoIndentation) + for (uint64_t it = 0; it < line.size() && isascii(line[it]) && isblank(line[it]); ++it) + newLine.push_back(line[it]); - if (m_languageDefinition.m_autoIndentation) - for (uint64_t it = 0; it < line.size() && isascii(line[it]) && isblank(line[it]); ++it) - newLine.push_back(line[it]); - - const uint64_t whitespaceSize = newLine.size(); - int32_t charStart = 0; - int32_t charPosition = 0; - auto charIndex = lineCoordinateToIndex(coord); - if (charIndex < whitespaceSize && m_languageDefinition.m_autoIndentation) { - charStart = (int32_t) whitespaceSize; - charPosition = charIndex; - } else { - charStart = charIndex; - charPosition = (int32_t) whitespaceSize; - } - newLine.insert(newLine.end(), line.begin() + charStart, line.end()); - line.erase(line.begin() + charStart,-1); - 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); - } else if (character == '\t') { - auto &line = m_lines[coord.m_line]; - auto charIndex = lineCoordinateToIndex(coord); - - if (!shift) { - auto spacesToInsert = m_tabSize - (charIndex % m_tabSize); - std::string spaces(spacesToInsert, ' '); - line.insert(line.begin() + charIndex, spaces.begin(), spaces.end()); + const uint64_t whitespaceSize = newLine.size(); + int32_t charStart = 0; + int32_t charPosition = 0; + auto charIndex = lineCoordinateToIndex(coord); + if (charIndex < whitespaceSize && m_languageDefinition.m_autoIndentation) { + charStart = (int32_t) whitespaceSize; + charPosition = charIndex; + } else { + charStart = charIndex; + charPosition = (int32_t) whitespaceSize; + } + newLine.insert(newLine.end(), line.begin() + charStart, line.end()); + line.erase(line.begin() + charStart, -1); line.m_colorized = false; - setCursorPosition(getCharacterCoordinates(coord.m_line, charIndex + spacesToInsert)); - } else { - auto spacesToRemove = (charIndex % m_tabSize); - if (spacesToRemove == 0) spacesToRemove = m_tabSize; - spacesToRemove = std::min(spacesToRemove, (int32_t) line.size()); - for (int32_t j = 0; j < spacesToRemove; j++) { - if (*(line.begin() + (charIndex - 1)) == ' ') { - line.erase(line.begin() + (charIndex - 1)); - charIndex -= 1; - } - } - line.m_colorized = false; - setCursorPosition(getCharacterCoordinates(coord.m_line, std::max(0, charIndex))); - } - u.m_addedSelection.m_end = setCoordinates(m_state.m_cursorPosition); - } else { - std::string buf = ""; - imTextCharToUtf8(buf, character); - if (buf.size() > 0) { - auto &line = m_lines[coord.m_line]; + setCursorPosition(getCharacterCoordinates(coord.m_line + 1, charPosition)); + u.m_added = (char) character; + u.m_addedSelection.m_end = setCoordinates(m_state.m_cursorPosition); + } else if (character == '\t') { + auto &line = m_lines[coord.m_line]; auto charIndex = lineCoordinateToIndex(coord); - if (m_overwrite && charIndex < (int32_t)line.size()) { - std::string c = line[coord.m_column]; - auto charCount = getStringCharacterCount(c); - auto d = c.size(); - - u.m_removedSelection = Selection( 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); + if (!shift) { + auto spacesToInsert = m_tabSize - (charIndex % m_tabSize); + std::string spaces(spacesToInsert, ' '); + line.insert(line.begin() + charIndex, spaces.begin(), spaces.end()); line.m_colorized = false; + setCursorPosition(getCharacterCoordinates(coord.m_line, charIndex + spacesToInsert)); + } else { + auto spacesToRemove = (charIndex % m_tabSize); + if (spacesToRemove == 0) spacesToRemove = m_tabSize; + spacesToRemove = std::min(spacesToRemove, (int32_t) line.size()); + for (int32_t j = 0; j < spacesToRemove; j++) { + if (*(line.begin() + (charIndex - 1)) == ' ') { + line.erase(line.begin() + (charIndex - 1)); + charIndex -= 1; + } + } + line.m_colorized = false; + setCursorPosition(getCharacterCoordinates(coord.m_line, std::max(0, charIndex))); } - auto charCount = getStringCharacterCount(buf); - if (buf == "{") - buf += "}"; - else if (buf == "[") - buf += "]"; - else if (buf == "(") - buf += ")"; - if ((buf == "}" || buf == "]" || buf == ")") && buf == line.substr(charIndex, charCount)) - buf = ""; + u.m_addedSelection.m_end = setCoordinates(m_state.m_cursorPosition); + } else { + std::string buf = ""; + imTextCharToUtf8(buf, character); + if (buf.size() > 0) { + auto &line = m_lines[coord.m_line]; + auto charIndex = lineCoordinateToIndex(coord); - if (buf == "\"") { - if (buf == line.substr(charIndex, charCount)) { - if (line.m_colors[charIndex + 1] == (char) PaletteIndex::StringLiteral) + if (m_overwrite && charIndex < (int32_t) line.size()) { + std::string c = line[coord.m_column]; + auto charCount = getStringCharacterCount(c); + auto d = c.size(); + + u.m_removedSelection = Selection(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); + if (buf == "{") + buf += "}"; + else if (buf == "[") + buf += "]"; + else if (buf == "(") + buf += ")"; + if ((buf == "}" || buf == "]" || buf == ")") && buf == line.substr(charIndex, charCount)) + buf = ""; + + if (buf == "\"") { + if (buf == line.substr(charIndex, charCount)) { + if (line.m_colors[charIndex + 1] == (char) PaletteIndex::StringLiteral) + buf += "\""; + else + buf = ""; + } else buf += "\""; - else - buf = ""; - } else - buf += "\""; - } + } - if (buf == "'") { - if (buf == line.substr(charIndex, charCount)) { - if (line.m_colors[charIndex + 1] == (char) PaletteIndex::CharLiteral) + if (buf == "'") { + if (buf == line.substr(charIndex, charCount)) { + if (line.m_colors[charIndex + 1] == (char) PaletteIndex::CharLiteral) + buf += "'"; + else + buf = ""; + } else buf += "'"; - else - buf = ""; - } else - buf += "'"; - } + } - 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()); - setCursorPosition(getCharacterCoordinates(coord.m_line, charIndex + charCount)); - } else - return; - } - u.m_after = m_state; + 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()); + setCursorPosition(getCharacterCoordinates(coord.m_line, charIndex + charCount)); + } else + return; + } + u.m_after = m_state; - m_textChanged = true; + m_textChanged = true; - addUndo(u); + addUndo(u); - colorize(); + colorize(); - std::string findWord = m_findReplaceHandler.getFindWord(); - if (!findWord.empty()) { - m_findReplaceHandler.resetMatches(); - m_findReplaceHandler.findAllMatches(this, findWord); - } + std::string findWord = m_findReplaceHandler.getFindWord(); + if (!findWord.empty()) { + m_findReplaceHandler.resetMatches(); + m_findReplaceHandler.findAllMatches(this, findWord); + } - ensureCursorVisible(); -} - -void TextEditor::setReadOnly(bool value) { - m_readOnly = value; -} - -void TextEditor::setCursorPosition(const Coordinates &position) { - if (m_state.m_cursorPosition != position) { - m_state.m_cursorPosition = position; ensureCursorVisible(); } -} -void TextEditor::setSelection(const Selection &selection) { - auto oldSelection = m_state.m_selection; - m_state.m_selection = setCoordinates(selection); - -} - -TextEditor::Selection TextEditor::getSelection() const { - return m_state.m_selection; -} - -void TextEditor::setTabSize(int32_t value) { - m_tabSize = std::max(0, std::min(32, value)); -} - -void TextEditor::insertText(const std::string &value) { - insertText(value.c_str()); -} - -void TextEditor::insertText(const char *value) { - if (value == nullptr) - return; - - auto pos = setCoordinates(m_state.m_cursorPosition); - auto start = std::min(pos, m_state.m_selection.m_start); - - insertTextAt(pos, value); - m_lines[pos.m_line].m_colorized = false; - - setSelection(Selection(pos, pos)); - setCursorPosition(pos); - - std::string findWord = m_findReplaceHandler.getFindWord(); - if (!findWord.empty()) { - m_findReplaceHandler.resetMatches(); - m_findReplaceHandler.findAllMatches(this, findWord); - } - colorize(); -} - -void TextEditor::deleteSelection() { - - if (m_state.m_selection.m_end == m_state.m_selection.m_start) - return; - - deleteRange(m_state.m_selection); - - setSelection(Selection(m_state.m_selection.m_start,m_state.m_selection.m_start)); - setCursorPosition(m_state.m_selection.m_start); - std::string findWord = m_findReplaceHandler.getFindWord(); - if (!findWord.empty()) { - m_findReplaceHandler.resetMatches(); - m_findReplaceHandler.findAllMatches(this, findWord); - } - colorize(); -} - -void TextEditor::jumpToLine(int32_t line) { - auto newPos = m_state.m_cursorPosition; - if (line != -1) { - newPos = setCoordinates(line , 0); - } - jumpToCoords(newPos); -} - -void TextEditor::jumpToCoords(const Coordinates &coords) { - setSelection(Selection(coords, coords)); - setCursorPosition(coords); - ensureCursorVisible(); - - setFocusAtCoords(coords); -} - -void TextEditor::moveToMatchedBracket(bool select) { - resetCursorBlinkTime(); - if (m_matchedBracket.isNearABracket(this, m_state.m_cursorPosition)) { - m_matchedBracket.findMatchingBracket(this); - auto oldPos = m_matchedBracket.m_nearCursor; - auto newPos = m_matchedBracket.m_matched; - if (newPos != setCoordinates(-1, -1)) { - if (select) { - if (oldPos == m_interactiveSelection.m_start) - m_interactiveSelection.m_start = newPos; - else if (oldPos == m_interactiveSelection.m_end) - m_interactiveSelection.m_end = newPos; - else { - m_interactiveSelection = Selection(newPos, oldPos); - } - } else - m_interactiveSelection.m_start = m_interactiveSelection.m_end = newPos; - - setSelection(m_interactiveSelection); - setCursorPosition(newPos); + void TextEditor::setCursorPosition(const Coordinates &position) { + if (m_state.m_cursorPosition != position) { + m_state.m_cursorPosition = position; ensureCursorVisible(); } } -} -void TextEditor::moveUp(int32_t amount, bool select) { - resetCursorBlinkTime(); - auto oldPos = m_state.m_cursorPosition; - if (amount < 0) { - m_scrollYIncrement = -1.0; - setScrollY(); - return; + void TextEditor::setSelection(const Selection &selection) { + auto oldSelection = m_state.m_selection; + m_state.m_selection = setCoordinates(selection); + } - m_state.m_cursorPosition.m_line = std::max(0, m_state.m_cursorPosition.m_line - amount); - if (oldPos != m_state.m_cursorPosition) { + + TextEditor::Selection TextEditor::getSelection() const { + return m_state.m_selection; + } + + void TextEditor::setTabSize(int32_t value) { + m_tabSize = std::max(0, std::min(32, value)); + } + + void TextEditor::insertText(const std::string &value) { + insertText(value.c_str()); + } + + void TextEditor::insertText(const char *value) { + if (value == nullptr) + return; + + auto pos = setCoordinates(m_state.m_cursorPosition); + auto start = std::min(pos, m_state.m_selection.m_start); + + insertTextAt(pos, value); + m_lines[pos.m_line].m_colorized = false; + + setSelection(Selection(pos, pos)); + setCursorPosition(pos); + + std::string findWord = m_findReplaceHandler.getFindWord(); + if (!findWord.empty()) { + m_findReplaceHandler.resetMatches(); + m_findReplaceHandler.findAllMatches(this, findWord); + } + colorize(); + } + + void TextEditor::deleteSelection() { + + if (m_state.m_selection.m_end == m_state.m_selection.m_start) + return; + + deleteRange(m_state.m_selection); + + setSelection(Selection(m_state.m_selection.m_start, m_state.m_selection.m_start)); + setCursorPosition(m_state.m_selection.m_start); + std::string findWord = m_findReplaceHandler.getFindWord(); + if (!findWord.empty()) { + m_findReplaceHandler.resetMatches(); + m_findReplaceHandler.findAllMatches(this, findWord); + } + colorize(); + } + + void TextEditor::jumpToLine(int32_t line) { + auto newPos = m_state.m_cursorPosition; + if (line != -1) { + newPos = setCoordinates(line, 0); + } + jumpToCoords(newPos); + } + + void TextEditor::jumpToCoords(const Coordinates &coords) { + setSelection(Selection(coords, coords)); + setCursorPosition(coords); + ensureCursorVisible(); + + setFocusAtCoords(coords); + } + + void TextEditor::moveToMatchedBracket(bool select) { + resetCursorBlinkTime(); + if (m_matchedBracket.isNearABracket(this, m_state.m_cursorPosition)) { + m_matchedBracket.findMatchingBracket(this); + auto oldPos = m_matchedBracket.m_nearCursor; + auto newPos = m_matchedBracket.m_matched; + if (newPos != setCoordinates(-1, -1)) { + if (select) { + if (oldPos == m_interactiveSelection.m_start) + m_interactiveSelection.m_start = newPos; + else if (oldPos == m_interactiveSelection.m_end) + m_interactiveSelection.m_end = newPos; + else { + m_interactiveSelection = Selection(newPos, oldPos); + } + } else + m_interactiveSelection.m_start = m_interactiveSelection.m_end = newPos; + + setSelection(m_interactiveSelection); + setCursorPosition(newPos); + ensureCursorVisible(); + } + } + } + + void TextEditor::moveUp(int32_t amount, bool select) { + resetCursorBlinkTime(); + auto oldPos = m_state.m_cursorPosition; + if (amount < 0) { + m_scrollYIncrement = -1.0; + setScrollY(); + return; + } + m_state.m_cursorPosition.m_line = std::max(0, m_state.m_cursorPosition.m_line - amount); + if (oldPos != m_state.m_cursorPosition) { + if (select) { + if (oldPos == m_interactiveSelection.m_start) + m_interactiveSelection.m_start = m_state.m_cursorPosition; + else if (oldPos == m_interactiveSelection.m_end) + m_interactiveSelection.m_end = m_state.m_cursorPosition; + else { + m_interactiveSelection.m_start = m_state.m_cursorPosition; + m_interactiveSelection.m_end = oldPos; + } + } else + m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; + setSelection(m_interactiveSelection); + + ensureCursorVisible(); + } + } + + void TextEditor::moveDown(int32_t amount, bool select) { + IM_ASSERT(m_state.m_cursorPosition.m_column >= 0); + resetCursorBlinkTime(); + auto oldPos = m_state.m_cursorPosition; + if (amount < 0) { + m_scrollYIncrement = 1.0; + setScrollY(); + return; + } + + m_state.m_cursorPosition.m_line = std::clamp(m_state.m_cursorPosition.m_line + amount, 0, (int32_t) m_lines.size() - 1); + if (oldPos.m_line == (m_lines.size() - 1)) { + m_topLine += amount; + m_topLine = std::clamp(m_topLine, 0.0F, m_lines.size() - 1.0F); + setTopLine(); + ensureCursorVisible(); + return; + } + + if (m_state.m_cursorPosition != oldPos) { + if (select) { + if (oldPos == m_interactiveSelection.m_end) + m_interactiveSelection.m_end = m_state.m_cursorPosition; + else if (oldPos == m_interactiveSelection.m_start) + m_interactiveSelection.m_start = m_state.m_cursorPosition; + else { + m_interactiveSelection.m_start = oldPos; + m_interactiveSelection.m_end = m_state.m_cursorPosition; + } + } else + m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; + setSelection(m_interactiveSelection); + + ensureCursorVisible(); + } + } + + void TextEditor::moveLeft(int32_t amount, bool select, bool wordMode) { + resetCursorBlinkTime(); + + auto oldPos = m_state.m_cursorPosition; + + + if (isEmpty() || oldPos.m_line >= m_lines.size()) + return; + + auto lindex = m_state.m_cursorPosition.m_line; + auto lineMaxColumn = getLineMaxColumn(lindex); + auto column = std::min(m_state.m_cursorPosition.m_column, lineMaxColumn); + + while (amount-- > 0) { + const auto &line = m_lines[lindex]; + if (column == 0) { + if (lindex == 0) + m_state.m_cursorPosition = Coordinates(0, 0); + else { + lindex--; + m_state.m_cursorPosition = setCoordinates(lindex, -1); + } + } else if (wordMode) + m_state.m_cursorPosition = findPreviousWord(m_state.m_cursorPosition); + else + m_state.m_cursorPosition = Coordinates(lindex, column - 1); + } + if (select) { if (oldPos == m_interactiveSelection.m_start) m_interactiveSelection.m_start = m_state.m_cursorPosition; @@ -1779,2251 +1848,2277 @@ void TextEditor::moveUp(int32_t amount, bool select) { m_interactiveSelection.m_end = m_state.m_cursorPosition; else { m_interactiveSelection.m_start = m_state.m_cursorPosition; - m_interactiveSelection.m_end = oldPos; + m_interactiveSelection.m_end = oldPos; } } else m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; + setSelection(m_interactiveSelection); ensureCursorVisible(); } -} -void TextEditor::moveDown(int32_t amount, bool select) { - IM_ASSERT(m_state.m_cursorPosition.m_column >= 0); - resetCursorBlinkTime(); - auto oldPos = m_state.m_cursorPosition; - if (amount < 0) { - m_scrollYIncrement = 1.0; - setScrollY(); - return; - } + void TextEditor::moveRight(int32_t amount, bool select, bool wordMode) { + resetCursorBlinkTime(); - m_state.m_cursorPosition.m_line = std::clamp(m_state.m_cursorPosition.m_line + amount, 0, (int32_t)m_lines.size() - 1); - if (oldPos.m_line == (m_lines.size() - 1)) { - m_topLine += amount; - m_topLine = std::clamp(m_topLine, 0.0F, m_lines.size() - 1.0F); - setTopLine(); - ensureCursorVisible(); - return; - } + auto oldPos = m_state.m_cursorPosition; + + if (isEmpty() || oldPos.m_line >= m_lines.size()) + return; + + auto lindex = m_state.m_cursorPosition.m_line; + auto lineMaxColumn = getLineMaxColumn(lindex); + auto column = std::min(m_state.m_cursorPosition.m_column, lineMaxColumn); + + while (amount-- > 0) { + const auto &line = m_lines[lindex]; + if (isEndOfLine(oldPos)) { + if (!isEndOfFile(oldPos)) { + lindex++; + m_state.m_cursorPosition = Coordinates(lindex, 0); + } else + m_state.m_cursorPosition = setCoordinates(-1, -1); + } else if (wordMode) + m_state.m_cursorPosition = findNextWord(m_state.m_cursorPosition); + else + m_state.m_cursorPosition = Coordinates(lindex, column + 1); + } - if (m_state.m_cursorPosition != oldPos) { if (select) { - if (oldPos == m_interactiveSelection.m_end) - m_interactiveSelection.m_end = m_state.m_cursorPosition; - else if (oldPos == m_interactiveSelection.m_start) + if (oldPos == m_interactiveSelection.m_end) { + m_interactiveSelection.m_end = Coordinates(m_state.m_cursorPosition); + if (m_interactiveSelection.m_end == Invalid) + return; + } else if (oldPos == m_interactiveSelection.m_start) m_interactiveSelection.m_start = m_state.m_cursorPosition; else { m_interactiveSelection.m_start = oldPos; - m_interactiveSelection.m_end = m_state.m_cursorPosition; + m_interactiveSelection.m_end = m_state.m_cursorPosition; } } else m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; + setSelection(m_interactiveSelection); ensureCursorVisible(); } -} -void TextEditor::moveLeft(int32_t amount, bool select, bool wordMode) { - resetCursorBlinkTime(); + void TextEditor::moveTop(bool select) { + resetCursorBlinkTime(); + auto oldPos = m_state.m_cursorPosition; + setCursorPosition(setCoordinates(0, 0)); - auto oldPos = m_state.m_cursorPosition; - - - if (isEmpty() || oldPos.m_line >= m_lines.size()) - return; - - auto lindex = m_state.m_cursorPosition.m_line; - auto lineMaxColumn = getLineMaxColumn(lindex); - auto column = std::min(m_state.m_cursorPosition.m_column, lineMaxColumn); - - while (amount-- > 0) { - const auto &line = m_lines[lindex]; - if (column == 0) { - if (lindex == 0) - m_state.m_cursorPosition = Coordinates(0, 0); - else { - lindex--; - m_state.m_cursorPosition = setCoordinates(lindex, -1); - } - } else if (wordMode) - m_state.m_cursorPosition = findPreviousWord(m_state.m_cursorPosition); - else - m_state.m_cursorPosition = Coordinates(lindex, column - 1); - } - - if (select) { - if (oldPos == m_interactiveSelection.m_start) - m_interactiveSelection.m_start = m_state.m_cursorPosition; - else if (oldPos == m_interactiveSelection.m_end) - m_interactiveSelection.m_end = m_state.m_cursorPosition; - else { - m_interactiveSelection.m_start = m_state.m_cursorPosition; - m_interactiveSelection.m_end = oldPos; - } - } else - m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; - - setSelection(m_interactiveSelection); - - ensureCursorVisible(); -} - -void TextEditor::moveRight(int32_t amount, bool select, bool wordMode) { - resetCursorBlinkTime(); - - auto oldPos = m_state.m_cursorPosition; - - if (isEmpty() || oldPos.m_line >= m_lines.size()) - return; - - auto lindex = m_state.m_cursorPosition.m_line; - auto lineMaxColumn = getLineMaxColumn(lindex); - auto column = std::min(m_state.m_cursorPosition.m_column, lineMaxColumn); - - while (amount-- > 0) { - const auto &line = m_lines[lindex]; - if (isEndOfLine(oldPos)) { - if (!isEndOfFile(oldPos)) { - lindex++; - m_state.m_cursorPosition = Coordinates(lindex, 0); + if (m_state.m_cursorPosition != oldPos) { + if (select) { + m_interactiveSelection = Selection(m_state.m_cursorPosition, oldPos); } else - m_state.m_cursorPosition = setCoordinates(-1, -1); - } else if (wordMode) - m_state.m_cursorPosition = findNextWord(m_state.m_cursorPosition); - else - m_state.m_cursorPosition = Coordinates(lindex, column + 1); + m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; + setSelection(m_interactiveSelection); + } } - if (select) { - if (oldPos == m_interactiveSelection.m_end) { - m_interactiveSelection.m_end = Coordinates(m_state.m_cursorPosition); - if (m_interactiveSelection.m_end == Invalid) - return; - } - else if (oldPos == m_interactiveSelection.m_start) - m_interactiveSelection.m_start = m_state.m_cursorPosition; - else { - m_interactiveSelection.m_start = oldPos; - m_interactiveSelection.m_end = m_state.m_cursorPosition; - } - } else - m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; - - setSelection(m_interactiveSelection); - - ensureCursorVisible(); -} - -void TextEditor::moveTop(bool select) { - resetCursorBlinkTime(); - auto oldPos = m_state.m_cursorPosition; - setCursorPosition(setCoordinates(0, 0)); - - if (m_state.m_cursorPosition != oldPos) { + void TextEditor::TextEditor::moveBottom(bool select) { + resetCursorBlinkTime(); + auto oldPos = getCursorPosition(); + auto newPos = setCoordinates(-1, -1); + setCursorPosition(newPos); if (select) { - m_interactiveSelection = Selection( m_state.m_cursorPosition, oldPos); + m_interactiveSelection = Selection(oldPos, newPos); } else - m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; + m_interactiveSelection.m_start = m_interactiveSelection.m_end = newPos; setSelection(m_interactiveSelection); } -} -void TextEditor::TextEditor::moveBottom(bool select) { - resetCursorBlinkTime(); - auto oldPos = getCursorPosition(); - auto newPos = setCoordinates(-1,-1); - setCursorPosition(newPos); - if (select) { - m_interactiveSelection = Selection( oldPos, newPos); - } else - m_interactiveSelection.m_start = m_interactiveSelection.m_end = newPos; - setSelection(m_interactiveSelection); -} + void TextEditor::moveHome(bool select) { + resetCursorBlinkTime(); + auto oldPos = m_state.m_cursorPosition; -void TextEditor::moveHome(bool select) { - resetCursorBlinkTime(); - auto oldPos = m_state.m_cursorPosition; - - auto &line = m_lines[oldPos.m_line]; - auto prefix = line.substr(0, oldPos.m_column); - auto postfix = line.substr(oldPos.m_column); - if (prefix.empty() && postfix.empty()) - return; - auto home=0; - if (!prefix.empty()) { - auto idx = prefix.find_first_not_of(" "); - if (idx == std::string::npos) { + auto &line = m_lines[oldPos.m_line]; + auto prefix = line.substr(0, oldPos.m_column); + auto postfix = line.substr(oldPos.m_column); + if (prefix.empty() && postfix.empty()) + return; + auto home = 0; + if (!prefix.empty()) { + auto idx = prefix.find_first_not_of(" "); + if (idx == std::string::npos) { + auto postIdx = postfix.find_first_of(" "); + if (postIdx == std::string::npos || postIdx == 0) + home = 0; + else { + postIdx = postfix.find_first_not_of(" "); + if (postIdx == std::string::npos) + home = getLineMaxColumn(oldPos.m_line); + else if (postIdx == 0) + home = 0; + else + home = oldPos.m_column + postIdx; + } + } else + home = idx; + } else { auto postIdx = postfix.find_first_of(" "); - if (postIdx == std::string::npos || postIdx == 0) - home=0; + if (postIdx == std::string::npos) + home = 0; else { postIdx = postfix.find_first_not_of(" "); if (postIdx == std::string::npos) home = getLineMaxColumn(oldPos.m_line); - else if (postIdx == 0) - home = 0; else home = oldPos.m_column + postIdx; } - } else - home = idx; - } else { - auto postIdx = postfix.find_first_of(" "); - if (postIdx == std::string::npos) - home = 0; - else { - postIdx = postfix.find_first_not_of(" "); - if (postIdx == std::string::npos) - home = getLineMaxColumn(oldPos.m_line); - else - home = oldPos.m_column + postIdx; - } - } - - setCursorPosition(Coordinates(m_state.m_cursorPosition.m_line, home)); - if (m_state.m_cursorPosition != oldPos) { - if (select) { - if (oldPos == m_interactiveSelection.m_start) - m_interactiveSelection.m_start = m_state.m_cursorPosition; - else if (oldPos == m_interactiveSelection.m_end) - m_interactiveSelection.m_end = m_state.m_cursorPosition; - else { - m_interactiveSelection.m_start = m_state.m_cursorPosition; - m_interactiveSelection.m_end = oldPos; - } - } else - m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; - setSelection(m_interactiveSelection); - } -} - -void TextEditor::moveEnd(bool select) { - resetCursorBlinkTime(); - auto oldPos = m_state.m_cursorPosition; - setCursorPosition(setCoordinates(m_state.m_cursorPosition.m_line, getLineMaxColumn(oldPos.m_line))); - - if (m_state.m_cursorPosition != oldPos) { - if (select) { - if (oldPos == m_interactiveSelection.m_end) - m_interactiveSelection.m_end = m_state.m_cursorPosition; - else if (oldPos == m_interactiveSelection.m_start) - m_interactiveSelection.m_start = m_state.m_cursorPosition; - else { - m_interactiveSelection.m_start = oldPos; - m_interactiveSelection.m_end = m_state.m_cursorPosition; - } - } else - m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; - setSelection(m_interactiveSelection); - } -} - -void TextEditor::deleteChar() { - resetCursorBlinkTime(); - IM_ASSERT(!m_readOnly); - - if (isEmpty()) - return; - - UndoRecord u; - u.m_before = m_state; - - if (hasSelection()) { - u.m_removed = getSelectedText(); - u.m_removedSelection = 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 == getLineMaxColumn(pos.m_line)) { - if (pos.m_line == (int32_t)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); - - auto &nextLine = m_lines[pos.m_line + 1]; - line.insert(line.end(), nextLine.begin(), nextLine.end()); - line.m_colorized = false; - removeLine(pos.m_line + 1); - - } else { - auto charIndex = lineCoordinateToIndex(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); - - auto d = utf8CharLength(line[charIndex][0]); - line.erase(line.begin() + charIndex, d); - line.m_colorized = false; } - m_textChanged = true; - - colorize(); - } - - u.m_after = m_state; - addUndo(u); - std::string findWord = m_findReplaceHandler.getFindWord(); - if (!findWord.empty()) { - m_findReplaceHandler.resetMatches(); - m_findReplaceHandler.findAllMatches(this, findWord); - } -} - -void TextEditor::backspace() { - resetCursorBlinkTime(); - if (isEmpty() || m_readOnly) - return; - - UndoRecord u; - u.m_before = m_state; - - if (hasSelection()) { - u.m_removed = getSelectedText(); - u.m_removedSelection = m_state.m_selection; - deleteSelection(); - } else { - auto pos = setCoordinates(m_state.m_cursorPosition); - auto &line = m_lines[pos.m_line]; - - if (pos.m_column == 0) { - if (pos.m_line == 0) - 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); - - auto &prevLine = m_lines[pos.m_line - 1]; - auto prevSize = getLineMaxColumn(pos.m_line - 1); - if (prevSize == 0) - prevLine = line; - else - prevLine.insert(prevLine.end(), line.begin(), line.end()); - prevLine.m_colorized = false; - - - ErrorMarkers errorMarker; - for (auto &i : m_errorMarkers) - errorMarker.insert(ErrorMarkers::value_type(i.first.m_line - 1 == m_state.m_cursorPosition.m_line ? setCoordinates(i.first.m_line - 1, i.first.m_column) : i.first, i.second)); - m_errorMarkers = std::move(errorMarker); - removeLine(m_state.m_cursorPosition.m_line); - --m_state.m_cursorPosition.m_line; - m_state.m_cursorPosition.m_column = prevSize; - } else { - pos.m_column -= 1; - std::string charToRemove = line[pos.m_column]; - if (pos.m_column < (int32_t)line.size() - 1) { - std::string charToRemoveNext = line[pos.m_column + 1]; - if (charToRemove == "{" && charToRemoveNext == "}") { - charToRemove += "}"; - m_state.m_cursorPosition.m_column += 1; - } else if (charToRemove == "[" && charToRemoveNext == "]") { - charToRemove += "]"; - m_state.m_cursorPosition.m_column += 1; - } else if (charToRemove == "(" && charToRemoveNext == ")") { - charToRemove += ")"; - m_state.m_cursorPosition.m_column += 1; - } else if (charToRemove == "\"" && charToRemoveNext == "\"") { - charToRemove += "\""; - m_state.m_cursorPosition.m_column += 1; - } else if (charToRemove == "'" && charToRemoveNext == "'") { - charToRemove += "'"; - m_state.m_cursorPosition.m_column += 1; + setCursorPosition(Coordinates(m_state.m_cursorPosition.m_line, home)); + if (m_state.m_cursorPosition != oldPos) { + if (select) { + if (oldPos == m_interactiveSelection.m_start) + m_interactiveSelection.m_start = m_state.m_cursorPosition; + else if (oldPos == m_interactiveSelection.m_end) + m_interactiveSelection.m_end = m_state.m_cursorPosition; + else { + m_interactiveSelection.m_start = m_state.m_cursorPosition; + m_interactiveSelection.m_end = oldPos; } - } - u.m_removedSelection = Selection( pos, m_state.m_cursorPosition); - u.m_removed = charToRemove; - auto charStart = lineCoordinateToIndex(pos); - auto charEnd = lineCoordinateToIndex(m_state.m_cursorPosition); - line.erase(line.begin() + charStart, charEnd - charStart); - m_state.m_cursorPosition = pos; - line.m_colorized = false; - } - - m_textChanged = true; - - ensureCursorVisible(); - colorize(); - } - - u.m_after = m_state; - addUndo(u); - std::string findWord = m_findReplaceHandler.getFindWord(); - if (!findWord.empty()) { - m_findReplaceHandler.resetMatches(); - m_findReplaceHandler.findAllMatches(this, findWord); - } -} - -void TextEditor::selectWordUnderCursor() { - auto wordStart = findWordStart(getCursorPosition()); - setSelection(Selection(wordStart, findWordEnd(wordStart))); -} - -void TextEditor::selectAll() { - setSelection(Selection(setCoordinates(0, 0), setCoordinates(-1, -1))); -} - -bool TextEditor::hasSelection() const { - return !isEmpty() && m_state.m_selection.m_end > m_state.m_selection.m_start; -} - -void TextEditor::copy() { - if (hasSelection()) { - ImGui::SetClipboardText(getSelectedText().c_str()); - } else { - if (!isEmpty()) { - std::string str; - const auto &line = m_lines[setCoordinates(m_state.m_cursorPosition).m_line]; - std::copy(line.m_chars.begin(), line.m_chars.end(), std::back_inserter(str)); - ImGui::SetClipboardText(str.c_str()); + } else + m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; + setSelection(m_interactiveSelection); } } -} -void TextEditor::cut() { - if (isReadOnly()) { - copy(); - } else { - if (!hasSelection()) { - auto lineIndex = setCoordinates(m_state.m_cursorPosition).m_line; - if (lineIndex < 0 || lineIndex >= (int32_t)m_lines.size()) - return; - setSelection(Selection(setCoordinates(lineIndex, 0), setCoordinates(lineIndex + 1, 0))); + void TextEditor::moveEnd(bool select) { + resetCursorBlinkTime(); + auto oldPos = m_state.m_cursorPosition; + setCursorPosition(setCoordinates(m_state.m_cursorPosition.m_line, getLineMaxColumn(oldPos.m_line))); + + if (m_state.m_cursorPosition != oldPos) { + if (select) { + if (oldPos == m_interactiveSelection.m_end) + m_interactiveSelection.m_end = m_state.m_cursorPosition; + else if (oldPos == m_interactiveSelection.m_start) + m_interactiveSelection.m_start = m_state.m_cursorPosition; + else { + m_interactiveSelection.m_start = oldPos; + m_interactiveSelection.m_end = m_state.m_cursorPosition; + } + } else + m_interactiveSelection.m_start = m_interactiveSelection.m_end = m_state.m_cursorPosition; + setSelection(m_interactiveSelection); } + } + + void TextEditor::deleteChar() { + resetCursorBlinkTime(); + IM_ASSERT(!m_readOnly); + + if (isEmpty()) + return; + UndoRecord u; u.m_before = m_state; - u.m_removed = getSelectedText(); - u.m_removedSelection = m_state.m_selection; - copy(); - deleteSelection(); + if (hasSelection()) { + u.m_removed = getSelectedText(); + u.m_removedSelection = 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 == getLineMaxColumn(pos.m_line)) { + if (pos.m_line == (int32_t) 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); + + auto &nextLine = m_lines[pos.m_line + 1]; + line.insert(line.end(), nextLine.begin(), nextLine.end()); + line.m_colorized = false; + removeLine(pos.m_line + 1); + + } else { + auto charIndex = lineCoordinateToIndex(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); + + auto d = utf8CharLength(line[charIndex][0]); + line.erase(line.begin() + charIndex, d); + line.m_colorized = false; + } + + m_textChanged = true; + + colorize(); + } u.m_after = m_state; addUndo(u); + std::string findWord = m_findReplaceHandler.getFindWord(); + if (!findWord.empty()) { + m_findReplaceHandler.resetMatches(); + m_findReplaceHandler.findAllMatches(this, findWord); + } } - std::string findWord = m_findReplaceHandler.getFindWord(); - if (!findWord.empty()) { - m_findReplaceHandler.resetMatches(); - m_findReplaceHandler.findAllMatches(this, findWord); - } -} -std::string TextEditor::replaceStrings(std::string string, const std::string &search, const std::string &replace) { - std::string result; - if (string.empty()) - return string; - if (search.empty()) { - for (auto c: string) { - if (c == '\0') { - result.append(replace); + void TextEditor::backspace() { + resetCursorBlinkTime(); + if (isEmpty() || m_readOnly) + return; + + UndoRecord u; + u.m_before = m_state; + + if (hasSelection()) { + u.m_removed = getSelectedText(); + u.m_removedSelection = m_state.m_selection; + deleteSelection(); + } else { + auto pos = setCoordinates(m_state.m_cursorPosition); + auto &line = m_lines[pos.m_line]; + + if (pos.m_column == 0) { + if (pos.m_line == 0) + 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); + + auto &prevLine = m_lines[pos.m_line - 1]; + auto prevSize = getLineMaxColumn(pos.m_line - 1); + if (prevSize == 0) + prevLine = line; + else + prevLine.insert(prevLine.end(), line.begin(), line.end()); + prevLine.m_colorized = false; + + + ErrorMarkers errorMarker; + for (auto &i: m_errorMarkers) + errorMarker.insert(ErrorMarkers::value_type(i.first.m_line - 1 == m_state.m_cursorPosition.m_line ? setCoordinates(i.first.m_line - 1, i.first.m_column) : i.first, i.second)); + m_errorMarkers = std::move(errorMarker); + removeLine(m_state.m_cursorPosition.m_line); + --m_state.m_cursorPosition.m_line; + m_state.m_cursorPosition.m_column = prevSize; } else { - result.push_back(c); + pos.m_column -= 1; + std::string charToRemove = line[pos.m_column]; + if (pos.m_column < (int32_t) line.size() - 1) { + std::string charToRemoveNext = line[pos.m_column + 1]; + if (charToRemove == "{" && charToRemoveNext == "}") { + charToRemove += "}"; + m_state.m_cursorPosition.m_column += 1; + } else if (charToRemove == "[" && charToRemoveNext == "]") { + charToRemove += "]"; + m_state.m_cursorPosition.m_column += 1; + } else if (charToRemove == "(" && charToRemoveNext == ")") { + charToRemove += ")"; + m_state.m_cursorPosition.m_column += 1; + } else if (charToRemove == "\"" && charToRemoveNext == "\"") { + charToRemove += "\""; + m_state.m_cursorPosition.m_column += 1; + } else if (charToRemove == "'" && charToRemoveNext == "'") { + charToRemove += "'"; + m_state.m_cursorPosition.m_column += 1; + } + } + u.m_removedSelection = Selection(pos, m_state.m_cursorPosition); + u.m_removed = charToRemove; + auto charStart = lineCoordinateToIndex(pos); + auto charEnd = lineCoordinateToIndex(m_state.m_cursorPosition); + line.erase(line.begin() + charStart, charEnd - charStart); + m_state.m_cursorPosition = pos; + line.m_colorized = false; + } + + m_textChanged = true; + + ensureCursorVisible(); + colorize(); + } + + u.m_after = m_state; + addUndo(u); + std::string findWord = m_findReplaceHandler.getFindWord(); + if (!findWord.empty()) { + m_findReplaceHandler.resetMatches(); + m_findReplaceHandler.findAllMatches(this, findWord); + } + } + + void TextEditor::selectWordUnderCursor() { + auto wordStart = findWordStart(getCursorPosition()); + setSelection(Selection(wordStart, findWordEnd(wordStart))); + } + + void TextEditor::selectAll() { + setSelection(Selection(setCoordinates(0, 0), setCoordinates(-1, -1))); + } + + bool TextEditor::hasSelection() const { + return !isEmpty() && m_state.m_selection.m_end > m_state.m_selection.m_start; + } + + void TextEditor::copy() { + if (hasSelection()) { + ImGui::SetClipboardText(getSelectedText().c_str()); + } else { + if (!isEmpty()) { + std::string str; + const auto &line = m_lines[setCoordinates(m_state.m_cursorPosition).m_line]; + std::copy(line.m_chars.begin(), line.m_chars.end(), std::back_inserter(str)); + ImGui::SetClipboardText(str.c_str()); } } - } else { - result = string; - std::uint64_t pos = 0; - while ((pos = result.find(search, pos)) != std::string::npos) { - result.replace(pos, search.size(), replace); - pos += replace.size(); - } } - return result; -} - -std::vector TextEditor::splitString(const std::string &string, const std::string &delimiter, bool removeEmpty) { - if (delimiter.empty() || string.empty()) { - return { string }; - } - - std::vector result; - - uint64_t start = 0, end = 0; - while ((end = string.find(delimiter, start)) != std::string::npos) { - uint64_t size = end - start; - if (start + size > string.length()) - break; - - auto token = string.substr(start, end - start); - start = end + delimiter.length(); - result.emplace_back(std::move(token)); - } - - if (start <= string.length()) - result.emplace_back(string.substr(start)); - - if (removeEmpty) - std::erase_if(result, [](const auto &string) { return string.empty(); }); - - return result; -} - - -std::string TextEditor::replaceTabsWithSpaces(const std::string& string, uint32_t tabSize) { - if (tabSize == 0 || string.empty() || string.find('\t') == std::string::npos) - return string; - - auto stringVector = splitString(string, "\n", false); - auto size = stringVector.size(); - std::string result; - for (uint64_t i = 0; i < size; i++) { - auto &line = stringVector[i]; - std::uint64_t pos = 0; - while ((pos = line.find('\t', pos)) != std::string::npos) { - auto spaces = tabSize - (pos % tabSize); - line.replace(pos, 1, std::string(spaces, ' ')); - pos += spaces - 1; - } - result += line; - if (i < size - 1) - result += "\n"; - } - return result; -} - - -std::string TextEditor::preprocessText(const std::string &code) { - std::string result = replaceStrings(code, "\r\n", "\n"); - result = replaceStrings(result, "\r", "\n"); - result = replaceTabsWithSpaces(result, m_tabSize); - - return result; -} - -void TextEditor::paste() { - if (isReadOnly()) - return; - - auto clipText = ImGui::GetClipboardText(); - if (clipText != nullptr ) { - auto len = strlen(clipText); - if (len > 0 ) { - std::string text = preprocessText(clipText); + void TextEditor::cut() { + if (m_readOnly) { + copy(); + } else { + if (!hasSelection()) { + auto lineIndex = setCoordinates(m_state.m_cursorPosition).m_line; + if (lineIndex < 0 || lineIndex >= (int32_t) m_lines.size()) + return; + setSelection(Selection(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; - if (hasSelection()) { - u.m_removed = getSelectedText(); - u.m_removedSelection = m_state.m_selection; - deleteSelection(); - } + copy(); + deleteSelection(); - u.m_added = text; - u.m_addedSelection.m_start = setCoordinates(m_state.m_cursorPosition); - insertText(text); - - u.m_addedSelection.m_end = setCoordinates(m_state.m_cursorPosition); u.m_after = m_state; addUndo(u); } + std::string findWord = m_findReplaceHandler.getFindWord(); + if (!findWord.empty()) { + m_findReplaceHandler.resetMatches(); + m_findReplaceHandler.findAllMatches(this, findWord); + } } - std::string findWord = m_findReplaceHandler.getFindWord(); - if (!findWord.empty()) { - m_findReplaceHandler.resetMatches(); - m_findReplaceHandler.findAllMatches(this, findWord); + + std::string TextEditor::replaceStrings(std::string string, const std::string &search, const std::string &replace) { + std::string result; + if (string.empty()) + return string; + if (search.empty()) { + for (auto c: string) { + if (c == '\0') { + result.append(replace); + } else { + result.push_back(c); + } + } + } else { + result = string; + std::uint64_t pos = 0; + while ((pos = result.find(search, pos)) != std::string::npos) { + result.replace(pos, search.size(), replace); + pos += replace.size(); + } + } + + return result; } -} -bool TextEditor::canUndo() { - return !m_readOnly && m_undoIndex > 0; -} + std::vector TextEditor::splitString(const std::string &string, const std::string &delimiter, bool removeEmpty) { + if (delimiter.empty() || string.empty()) { + return {string}; + } -bool TextEditor::canRedo() const { - return !m_readOnly && m_undoIndex < (int32_t)m_undoBuffer.size(); -} + std::vector result; -void TextEditor::undo(int32_t steps) { - while (canUndo() && steps-- > 0) - m_undoBuffer[--m_undoIndex].undo(this); - std::string findWord = m_findReplaceHandler.getFindWord(); - if (!findWord.empty()) { - m_findReplaceHandler.resetMatches(); - m_findReplaceHandler.findAllMatches(this, findWord); + uint64_t start = 0, end = 0; + while ((end = string.find(delimiter, start)) != std::string::npos) { + uint64_t size = end - start; + if (start + size > string.length()) + break; + + auto token = string.substr(start, end - start); + start = end + delimiter.length(); + result.emplace_back(std::move(token)); + } + + if (start <= string.length()) + result.emplace_back(string.substr(start)); + + if (removeEmpty) + std::erase_if(result, [](const auto &string) { return string.empty(); }); + + return result; } -} -void TextEditor::redo(int32_t steps) { - while (canRedo() && steps-- > 0) - m_undoBuffer[m_undoIndex++].redo(this); - std::string findWord = m_findReplaceHandler.getFindWord(); - if (!findWord.empty()) { - m_findReplaceHandler.resetMatches(); - m_findReplaceHandler.findAllMatches(this, findWord); + + std::string TextEditor::replaceTabsWithSpaces(const std::string &string, uint32_t tabSize) { + if (tabSize == 0 || string.empty() || string.find('\t') == std::string::npos) + return string; + + auto stringVector = splitString(string, "\n", false); + auto size = stringVector.size(); + std::string result; + for (uint64_t i = 0; i < size; i++) { + auto &line = stringVector[i]; + std::uint64_t pos = 0; + while ((pos = line.find('\t', pos)) != std::string::npos) { + auto spaces = tabSize - (pos % tabSize); + line.replace(pos, 1, std::string(spaces, ' ')); + pos += spaces - 1; + } + result += line; + if (i < size - 1) + result += "\n"; + } + return result; + } + + + std::string TextEditor::preprocessText(const std::string &code) { + std::string result = replaceStrings(code, "\r\n", "\n"); + result = replaceStrings(result, "\r", "\n"); + result = replaceTabsWithSpaces(result, m_tabSize); + + return result; + } + + void TextEditor::paste() { + if (m_readOnly) + return; + + auto clipText = ImGui::GetClipboardText(); + if (clipText != nullptr) { + auto len = strlen(clipText); + if (len > 0) { + std::string text = preprocessText(clipText); + UndoRecord u; + u.m_before = m_state; + + if (hasSelection()) { + u.m_removed = getSelectedText(); + u.m_removedSelection = m_state.m_selection; + deleteSelection(); + } + + u.m_added = text; + u.m_addedSelection.m_start = setCoordinates(m_state.m_cursorPosition); + insertText(text); + + u.m_addedSelection.m_end = setCoordinates(m_state.m_cursorPosition); + u.m_after = m_state; + addUndo(u); + } + } + std::string findWord = m_findReplaceHandler.getFindWord(); + if (!findWord.empty()) { + m_findReplaceHandler.resetMatches(); + m_findReplaceHandler.findAllMatches(this, findWord); + } + } + + bool TextEditor::canUndo() { + return !m_readOnly && m_undoIndex > 0; + } + + bool TextEditor::canRedo() const { + return !m_readOnly && m_undoIndex < (int32_t) m_undoBuffer.size(); + } + + void TextEditor::undo(int32_t steps) { + while (canUndo() && steps-- > 0) + m_undoBuffer[--m_undoIndex].undo(this); + std::string findWord = m_findReplaceHandler.getFindWord(); + if (!findWord.empty()) { + m_findReplaceHandler.resetMatches(); + m_findReplaceHandler.findAllMatches(this, findWord); + } + } + + void TextEditor::redo(int32_t steps) { + while (canRedo() && steps-- > 0) + m_undoBuffer[m_undoIndex++].redo(this); + std::string findWord = m_findReplaceHandler.getFindWord(); + if (!findWord.empty()) { + m_findReplaceHandler.resetMatches(); + m_findReplaceHandler.findAllMatches(this, findWord); + } } -} // the index here is array index so zero based -void TextEditor::FindReplaceHandler::selectFound(TextEditor *editor, int32_t found) { - IM_ASSERT(found >= 0 && found < m_matches.size()); - auto selection = m_matches[found].m_selection; - editor->setSelection(selection); - editor->setCursorPosition(selection.m_end); - editor->ensureCursorVisible(); -} + void TextEditor::FindReplaceHandler::selectFound(TextEditor *editor, int32_t found) { + IM_ASSERT(found >= 0 && found < m_matches.size()); + auto selection = m_matches[found].m_selection; + editor->setSelection(selection); + editor->setCursorPosition(selection.m_end); + editor->ensureCursorVisible(); + } // The returned index is shown in the form // 'index of count' so 1 based -uint32_t TextEditor::FindReplaceHandler::findMatch(TextEditor *editor, bool isNex) { + uint32_t TextEditor::FindReplaceHandler::findMatch(TextEditor *editor, bool isNex) { - if (editor->m_textChanged || m_optionsChanged) { - std::string findWord = getFindWord(); - if (findWord.empty()) + if (editor->m_textChanged || m_optionsChanged) { + std::string findWord = getFindWord(); + if (findWord.empty()) + return 0; + resetMatches(); + findAllMatches(editor, findWord); + } + + auto targetPos = editor->m_state.m_cursorPosition; + auto count = m_matches.size(); + + if (count == 0) { + editor->setCursorPosition(targetPos); return 0; - resetMatches(); - findAllMatches(editor, findWord); - } + } - auto targetPos = editor->m_state.m_cursorPosition; - auto count = m_matches.size(); - - if (count == 0) { - editor->setCursorPosition(targetPos); - return 0; - } - - for (uint32_t i=0; i < count; i++) { - if (targetPos >= m_matches[i].m_selection.m_start && targetPos <= m_matches[i].m_selection.m_end) { - if (isNex) { - if (i == count - 1) { - selectFound(editor, 0); - return 1; + for (uint32_t i = 0; i < count; i++) { + if (targetPos >= m_matches[i].m_selection.m_start && targetPos <= m_matches[i].m_selection.m_end) { + if (isNex) { + if (i == count - 1) { + selectFound(editor, 0); + return 1; + } else { + selectFound(editor, i + 1); + return (i + 2); + } } else { - selectFound(editor, i + 1); - return (i + 2); + if (i == 0) { + selectFound(editor, count - 1); + return count; + } else { + selectFound(editor, i - 1); + return i; + } } + } + } + + if ((targetPos > m_matches[count - 1].m_selection.m_end) || (targetPos < m_matches[0].m_selection.m_start)) { + if (isNex) { + selectFound(editor, 0); + return 1; } else { - if (i == 0) { - selectFound(editor, count - 1); - return count; + selectFound(editor, count - 1); + return count; + } + } + + for (uint32_t i = 1; i < count; i++) { + + if (m_matches[i - 1].m_selection.m_end <= targetPos && m_matches[i].m_selection.m_start >= targetPos) { + if (isNex) { + selectFound(editor, i); + return i + 1; } else { selectFound(editor, i - 1); return i; } } } + + return 0; } - if ((targetPos > m_matches[count - 1].m_selection.m_end) || (targetPos < m_matches[0].m_selection.m_start)) { - if (isNex) { - selectFound(editor, 0); - return 1; - } else { - selectFound(editor, count - 1); - return count; - } - } - - for (uint32_t i=1;i < count;i++) { - - if (m_matches[i - 1].m_selection.m_end <= targetPos && - m_matches[i].m_selection.m_start >= targetPos ) { - if (isNex) { - selectFound(editor, i); - return i + 1; - } else { - selectFound(editor, i - 1); - return i; - } - } - } - - return 0; -} - // returns 1 based index -uint32_t TextEditor::FindReplaceHandler::findPosition(TextEditor *editor, Coordinates pos, bool isNext) { - if (editor->m_textChanged || m_optionsChanged) { - std::string findWord = getFindWord(); - if (findWord.empty()) - return 0; - resetMatches(); - findAllMatches(editor, findWord); - } + uint32_t TextEditor::FindReplaceHandler::findPosition(TextEditor *editor, Coordinates pos, bool isNext) { + if (editor->m_textChanged || m_optionsChanged) { + std::string findWord = getFindWord(); + if (findWord.empty()) + return 0; + resetMatches(); + findAllMatches(editor, findWord); + } - int32_t count = m_matches.size(); - if (count == 0) + int32_t count = m_matches.size(); + if (count == 0) + return 0; + if (isNext) { + if (pos > m_matches[count - 1].m_selection.m_end || pos <= m_matches[0].m_selection.m_end) + return 1; + for (uint32_t i = 1; i < count; i++) { + if (pos > m_matches[i - 1].m_selection.m_end && pos <= m_matches[i].m_selection.m_end) + return i + 1; + } + } else { + if (pos >= m_matches[count - 1].m_selection.m_start || pos < m_matches[0].m_selection.m_start) + return count; + for (uint32_t i = 1; i < count; i++) { + if (pos >= m_matches[i - 1].m_selection.m_start && pos < m_matches[i].m_selection.m_start) + return i; + } + } return 0; - if( isNext) { - if (pos > m_matches[count - 1].m_selection.m_end || pos <= m_matches[0].m_selection.m_end) - return 1; - for (uint32_t i = 1; i < count; i++) { - if (pos > m_matches[i - 1].m_selection.m_end && pos <= m_matches[i].m_selection.m_end) - return i+1; - } - } else { - if (pos >= m_matches[count - 1].m_selection.m_start || pos < m_matches[0].m_selection.m_start) - return count; - for (uint32_t i = 1; i < count; i++) { - if (pos >= m_matches[i - 1].m_selection.m_start && pos < m_matches[i].m_selection.m_start) - return i ; - } } - return 0; -} // Create a string that escapes special characters // and separate word from non word -std::string make_wholeWord(const std::string &s) { - static const char metacharacters[] = R"(\.^$-+()[]{}|?*)"; - std::string out; - out.reserve(s.size()); - if (s[0] == '#') - out.push_back('#'); - out.push_back('\\'); - out.push_back('b'); - for (auto ch : s) { - if (strchr(metacharacters, ch)) - out.push_back('\\'); - out.push_back(ch); + std::string make_wholeWord(const std::string &s) { + static const char metacharacters[] = R"(\.^$-+()[]{}|?*)"; + std::string out; + out.reserve(s.size()); + if (s[0] == '#') + out.push_back('#'); + out.push_back('\\'); + out.push_back('b'); + for (auto ch: s) { + if (strchr(metacharacters, ch)) + out.push_back('\\'); + out.push_back(ch); + } + out.push_back('\\'); + out.push_back('b'); + return out; } - out.push_back('\\'); - out.push_back('b'); - return out; -} // Performs actual search to fill mMatches -bool TextEditor::FindReplaceHandler::findNext(TextEditor *editor) { - Coordinates curPos; - curPos.m_line = m_matches.empty() ? editor->m_state.m_cursorPosition.m_line : m_matches.back().m_cursorPosition.m_line; - curPos.m_column = m_matches.empty() ? editor->m_state.m_cursorPosition.m_column : editor->lineCoordinateToIndex(m_matches.back().m_cursorPosition); + bool TextEditor::FindReplaceHandler::findNext(TextEditor *editor) { + Coordinates curPos; + curPos.m_line = m_matches.empty() ? editor->m_state.m_cursorPosition.m_line : m_matches.back().m_cursorPosition.m_line; + curPos.m_column = m_matches.empty() ? editor->m_state.m_cursorPosition.m_column : editor->lineCoordinateToIndex(m_matches.back().m_cursorPosition); - uint64_t matchLength = getStringCharacterCount(m_findWord); - uint64_t byteIndex = 0; + uint64_t matchLength = getStringCharacterCount(m_findWord); + uint64_t byteIndex = 0; - for (uint64_t ln = 0; ln < curPos.m_line; ln++) - byteIndex += editor->getLineByteCount(ln) + 1; - byteIndex += curPos.m_column; + for (uint64_t ln = 0; ln < curPos.m_line; ln++) + byteIndex += editor->getLineByteCount(ln) + 1; + byteIndex += curPos.m_column; - std::string wordLower = m_findWord; - if (!getMatchCase()) - std::transform(wordLower.begin(), wordLower.end(), wordLower.begin(), ::tolower); + std::string wordLower = m_findWord; + if (!getMatchCase()) + std::transform(wordLower.begin(), wordLower.end(), wordLower.begin(), ::tolower); - std::string textSrc = editor->getText(); - if (!getMatchCase()) - std::transform(textSrc.begin(), textSrc.end(), textSrc.begin(), ::tolower); + std::string textSrc = editor->getText(); + if (!getMatchCase()) + std::transform(textSrc.begin(), textSrc.end(), textSrc.begin(), ::tolower); - uint64_t textLoc; - // TODO: use regexp find iterator in all cases - // to find all matches for FindAllMatches. - // That should make things faster (no need - // to call FindNext many times) and remove - // clunky match case code - if (getWholeWord() || getFindRegEx()) { - std::regex regularExpression; - if (getFindRegEx()) { - try { - regularExpression.assign(wordLower); - } catch (const std::regex_error &e) { - return false; + uint64_t textLoc; + // TODO: use regexp find iterator in all cases + // to find all matches for FindAllMatches. + // That should make things faster (no need + // to call FindNext many times) and remove + // clunky match case code + if (getWholeWord() || getFindRegEx()) { + std::regex regularExpression; + if (getFindRegEx()) { + try { + regularExpression.assign(wordLower); + } catch (const std::regex_error &e) { + return false; + } + } else { + try { + regularExpression.assign(make_wholeWord(wordLower)); + } catch (const std::regex_error &e) { + return false; + } } + + uint64_t pos = 0; + std::sregex_iterator iter = std::sregex_iterator(textSrc.begin(), textSrc.end(), regularExpression); + std::sregex_iterator end; + if (!iter->ready()) + return false; + uint64_t firstLoc = iter->position(); + uint64_t firstLength = iter->length(); + + if (firstLoc > byteIndex) { + pos = firstLoc; + matchLength = firstLength; + } else { + + while (iter != end) { + iter++; + if (((pos = iter->position()) > byteIndex) && ((matchLength = iter->length()) > 0)) + break; + } + } + + if (iter == end) + return false; + + textLoc = pos; } else { - try { - regularExpression.assign(make_wholeWord(wordLower)); - } catch (const std::regex_error &e) { + // non regex search + textLoc = textSrc.find(wordLower, byteIndex); + if (textLoc == std::string::npos) return false; - } } - - uint64_t pos=0; - std::sregex_iterator iter = std::sregex_iterator(textSrc.begin(), textSrc.end(), regularExpression); - std::sregex_iterator end; - if (!iter->ready()) - return false; - uint64_t firstLoc = iter->position(); - uint64_t firstLength = iter->length(); - - if(firstLoc > byteIndex) { - pos = firstLoc; - matchLength = firstLength; - } else { - - while (iter != end) { - iter++; - if (((pos = iter->position()) > byteIndex) && ((matchLength = iter->length()) > 0)) - break; - } - } - - if (iter == end) - return false; - - textLoc = pos; - } else { - // non regex search - textLoc = textSrc.find(wordLower, byteIndex); if (textLoc == std::string::npos) return false; + TextEditor::EditorState state; + state.m_selection = Selection(stringIndexToCoordinates(textLoc, textSrc), stringIndexToCoordinates(textLoc + matchLength, textSrc)); + state.m_cursorPosition = state.m_selection.m_end; + m_matches.push_back(state); + return true; } - if (textLoc == std::string::npos) - return false; - TextEditor::EditorState state; - state.m_selection = Selection(stringIndexToCoordinates(textLoc, textSrc), - stringIndexToCoordinates(textLoc + matchLength, 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 TextEditor::FindReplaceHandler::findAllMatches(TextEditor *editor, std::string findWord) { + + if (findWord.empty()) { + editor->ensureCursorVisible(); + m_findWord = ""; + m_matches.clear(); + return; + } + + if (findWord == m_findWord && !editor->m_textChanged && !m_optionsChanged) + return; + + if (m_optionsChanged) + m_optionsChanged = false; - if (findWord.empty()) { - editor->ensureCursorVisible(); - m_findWord = ""; m_matches.clear(); - return; - } + m_findWord = findWord; + auto startingPos = editor->m_state.m_cursorPosition; + auto saveState = editor->m_state; + Coordinates begin = editor->setCoordinates(0, 0); + editor->m_state.m_cursorPosition = begin; - if(findWord == m_findWord && !editor->m_textChanged && !m_optionsChanged) - return; - - if (m_optionsChanged) - m_optionsChanged = false; - - m_matches.clear(); - m_findWord = findWord; - auto startingPos = editor->m_state.m_cursorPosition; - auto saveState = editor->m_state; - Coordinates begin = editor->setCoordinates(0,0); - editor->m_state.m_cursorPosition = begin; - - if (!findNext(editor)) { - editor->m_state = saveState; - editor->ensureCursorVisible(); - return; - } - TextEditor::EditorState state = m_matches.back(); - - while(state.m_cursorPosition < startingPos) { if (!findNext(editor)) { editor->m_state = saveState; editor->ensureCursorVisible(); return; } - state = m_matches.back(); - } + TextEditor::EditorState state = m_matches.back(); - while (findNext(editor)); + while (state.m_cursorPosition < startingPos) { + if (!findNext(editor)) { + editor->m_state = saveState; + editor->ensureCursorVisible(); + return; + } + state = m_matches.back(); + } - editor->m_state = saveState; - editor->ensureCursorVisible(); - return; -} - - -bool TextEditor::FindReplaceHandler::replace(TextEditor *editor, bool right) { - if (m_matches.empty() || m_findWord == m_replaceWord || m_findWord.empty()) - return false; - - - auto state = editor->m_state; - - if (editor->m_state.m_cursorPosition <= editor->m_state.m_selection.m_end && editor->m_state.m_selection.m_end > editor->m_state.m_selection.m_start && editor->m_state.m_cursorPosition > editor->m_state.m_selection.m_start) { - - editor->m_state.m_cursorPosition = editor->m_state.m_selection.m_start; - if(editor->m_state.m_cursorPosition.m_column == 0) { - editor->m_state.m_cursorPosition.m_line--; - editor->m_state.m_cursorPosition.m_column = editor->getLineMaxColumn( - editor->m_state.m_cursorPosition.m_line); - } else - editor->m_state.m_cursorPosition.m_column--; - } - auto matchIndex = findMatch(editor, right); - if(matchIndex != 0) { - UndoRecord u; - u.m_before = editor->m_state; - - auto selectionEnd = editor->m_state.m_selection.m_end; - - u.m_removed = editor->getSelectedText(); - u.m_removedSelection = 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); - u.m_added = replacedText; - } else - u.m_added = m_replaceWord; - - u.m_addedSelection.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); + while (findNext(editor)); + editor->m_state = saveState; editor->ensureCursorVisible(); - ImGui::SetKeyboardFocusHere(0); + return; + } - u.m_after = editor->m_state; - editor->addUndo(u); - editor->m_textChanged = true; + + bool TextEditor::FindReplaceHandler::replace(TextEditor *editor, bool right) { + if (m_matches.empty() || m_findWord == m_replaceWord || m_findWord.empty()) + return false; + + + auto state = editor->m_state; + + if (editor->m_state.m_cursorPosition <= editor->m_state.m_selection.m_end && + editor->m_state.m_selection.m_end > editor->m_state.m_selection.m_start && + editor->m_state.m_cursorPosition > editor->m_state.m_selection.m_start) { + + editor->m_state.m_cursorPosition = editor->m_state.m_selection.m_start; + if (editor->m_state.m_cursorPosition.m_column == 0) { + editor->m_state.m_cursorPosition.m_line--; + editor->m_state.m_cursorPosition.m_column = editor->getLineMaxColumn(editor->m_state.m_cursorPosition.m_line); + } else + editor->m_state.m_cursorPosition.m_column--; + } + auto matchIndex = findMatch(editor, right); + if (matchIndex != 0) { + UndoRecord u; + u.m_before = editor->m_state; + + auto selectionEnd = editor->m_state.m_selection.m_end; + + u.m_removed = editor->getSelectedText(); + u.m_removedSelection = 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); + u.m_added = replacedText; + } else + u.m_added = m_replaceWord; + + u.m_addedSelection.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); + + editor->ensureCursorVisible(); + ImGui::SetKeyboardFocusHere(0); + + u.m_after = editor->m_state; + editor->addUndo(u); + editor->m_textChanged = true; + + return true; + } + editor->m_state = state; + return false; + } + + bool TextEditor::FindReplaceHandler::replaceAll(TextEditor *editor) { + uint32_t count = m_matches.size(); + + for (uint32_t i = 0; i < count; i++) + replace(editor, true); return true; } - editor->m_state = state; - return false; -} -bool TextEditor::FindReplaceHandler::replaceAll(TextEditor *editor) { - uint32_t count = m_matches.size(); - - for (uint32_t i = 0; i < count; i++) - replace(editor, true); - - return true; -} - -const TextEditor::Palette &TextEditor::getDarkPalette() { - const static Palette p = { - { - 0xff7f7f7f, // Default - 0xffd69c56, // Keyword - 0xff00ff00, // Number - 0xff7070e0, // String - 0xff70a0e0, // Char literal - 0xffffffff, // Punctuation - 0xff408080, // Preprocessor - 0xffaaaaaa, // Identifier - 0xff9bc64d, // Known identifier - 0xffc040a0, // Preproc identifier - 0xff708020, // Global Doc Comment - 0xff586820, // Doc Comment - 0xff206020, // Comment (single line) - 0xff406020, // Comment (multi line) - 0xff004545, // Preprocessor deactivated - 0xff101010, // Background - 0xffe0e0e0, // Cursor - 0x80a06020, // Selection - 0x800020ff, // ErrorMarker - 0x40f08000, // Breakpoint - 0xff707000, // Line number - 0x40000000, // Current line fill - 0x40808080, // Current line fill (inactive) - 0x40a0a0a0, // Current line edge - } - }; - return p; -} - -const TextEditor::Palette &TextEditor::getLightPalette() { - const static Palette p = { - { - 0xff7f7f7f, // None - 0xffff0c06, // Keyword - 0xff008000, // Number - 0xff2020a0, // String - 0xff304070, // Char literal - 0xff000000, // Punctuation - 0xff406060, // Preprocessor - 0xff404040, // Identifier - 0xff606010, // Known identifier - 0xffc040a0, // Preproc identifier - 0xff707820, // Global Doc Comment - 0xff586020, // Doc Comment - 0xff205020, // Comment (single line) - 0xff405020, // Comment (multi line) - 0xffa7cccc, // Preprocessor deactivated - 0xffffffff, // Background - 0xff000000, // Cursor - 0x80600000, // Selection - 0xa00010ff, // ErrorMarker - 0x80f08000, // Breakpoint - 0xff505000, // Line number - 0x40000000, // Current line fill - 0x40808080, // Current line fill (inactive) - 0x40000000, // Current line edge - } - }; - return p; -} - -const TextEditor::Palette &TextEditor::getRetroBluePalette() { - const static Palette p = { - { - 0xff00ffff, // None - 0xffffff00, // Keyword - 0xff00ff00, // Number - 0xff808000, // String - 0xff808000, // Char literal - 0xffffffff, // Punctuation - 0xff008000, // Preprocessor - 0xff00ffff, // Identifier - 0xffffffff, // Known identifier - 0xffff00ff, // Preproc identifier - 0xff101010, // Global Doc Comment - 0xff202020, // Doc Comment - 0xff808080, // Comment (single line) - 0xff404040, // Comment (multi line) - 0xff004000, // Preprocessor deactivated - 0xff800000, // Background - 0xff0080ff, // Cursor - 0x80ffff00, // Selection - 0xa00000ff, // ErrorMarker - 0x80ff8000, // Breakpoint - 0xff808000, // Line number - 0x40000000, // Current line fill - 0x40808080, // Current line fill (inactive) - 0x40000000, // Current line edge - } - }; - return p; -} - - -std::string TextEditor::getText() const { - auto start = setCoordinates(0, 0); - auto end = setCoordinates(-1, -1); - if (start == Invalid || end == Invalid) - return ""; - return getText(Selection(start, end)); -} - -std::vector TextEditor::getTextLines() const { - std::vector result; - - result.reserve(m_lines.size()); - - for (const auto &line : m_lines) { - std::string text = line.m_chars; - result.emplace_back(std::move(text)); + const TextEditor::Palette &TextEditor::getDarkPalette() { + const static Palette p = { + { + 0xff7f7f7f, // Default + 0xffd69c56, // Keyword + 0xff00ff00, // Number + 0xff7070e0, // String + 0xff70a0e0, // Char literal + 0xffffffff, // Punctuation + 0xff408080, // Preprocessor + 0xffaaaaaa, // Identifier + 0xff9bc64d, // Known identifier + 0xffc040a0, // Preproc identifier + 0xff708020, // Global Doc Comment + 0xff586820, // Doc Comment + 0xff206020, // Comment (single line) + 0xff406020, // Comment (multi line) + 0xff004545, // Preprocessor deactivated + 0xff101010, // Background + 0xffe0e0e0, // Cursor + 0x80a06020, // Selection + 0x800020ff, // ErrorMarker + 0x40f08000, // Breakpoint + 0xff707000, // Line number + 0x40000000, // Current line fill + 0x40808080, // Current line fill (inactive) + 0x40a0a0a0, // Current line edge + } + }; + return p; } - return result; -} - -std::string TextEditor::getSelectedText() const { - return getText(m_state.m_selection); -} - -std::string TextEditor::getLineText(int32_t line) const { - auto sanitizedLine = setCoordinates(line, 0); - auto endLine = setCoordinates(line, -1); - if (sanitizedLine == Invalid || endLine == Invalid) - return ""; - return getText(Selection(sanitizedLine, endLine)); -} - -void TextEditor::colorize() { - m_updateFlags = true; -} - -void TextEditor::colorizeRange() { - - if (isEmpty()) - return; - - std::smatch results; - std::string id; - if (m_languageDefinition.m_tokenize == nullptr) - m_languageDefinition.m_tokenize = [](strConstIter, strConstIter, strConstIter &, strConstIter &, PaletteIndex &) { return false; }; - auto linesSize = m_lines.size(); - for (int32_t i = 0; i < linesSize; ++i) { - auto &line = m_lines[i]; - auto size = line.size(); - - if (line.m_colors.size() != size) { - line.m_colors.resize(size, 0); - line.m_colorized = false; - } - - if (line.m_colorized || line.empty()) - continue; - - auto last = line.end(); - - auto first = line.begin(); - for (auto current = first;(current-first) < size;) { - strConstIter token_begin; - strConstIter token_end; - PaletteIndex token_color = PaletteIndex::Default; - - bool hasTokenizeResult = m_languageDefinition.m_tokenize(current.m_charsIter, last.m_charsIter, token_begin, token_end, token_color); - auto token_offset = token_begin - first.m_charsIter; - - if (!hasTokenizeResult) { - // todo : remove - // printf("using regex for %.*s\n", first + 10 < last ? 10 : int32_t(last - first), first); - - for (auto &p : m_regexList) { - if (std::regex_search(first.m_charsIter, last.m_charsIter, results, p.first, std::regex_constants::match_continuous)) { - hasTokenizeResult = true; - - const auto &v = results.begin(); - token_begin = v->first; - token_end = v->second; - token_color = p.second; - break; - } + const TextEditor::Palette &TextEditor::getLightPalette() { + const static Palette p = { + { + 0xff7f7f7f, // None + 0xffff0c06, // Keyword + 0xff008000, // Number + 0xff2020a0, // String + 0xff304070, // Char literal + 0xff000000, // Punctuation + 0xff406060, // Preprocessor + 0xff404040, // Identifier + 0xff606010, // Known identifier + 0xffc040a0, // Preproc identifier + 0xff707820, // Global Doc Comment + 0xff586020, // Doc Comment + 0xff205020, // Comment (single line) + 0xff405020, // Comment (multi line) + 0xffa7cccc, // Preprocessor deactivated + 0xffffffff, // Background + 0xff000000, // Cursor + 0x80600000, // Selection + 0xa00010ff, // ErrorMarker + 0x80f08000, // Breakpoint + 0xff505000, // Line number + 0x40000000, // Current line fill + 0x40808080, // Current line fill (inactive) + 0x40000000, // Current line edge } - } - - if (!hasTokenizeResult) - current=current + 1; - else { - current = first + token_offset; - uint64_t token_length=0; - Line::Flags flags(0); - flags.m_value = line.m_flags[token_offset]; - if (flags.m_value == 0) { - token_length = token_end - token_begin; - if (token_color == PaletteIndex::Identifier) { - id.assign(token_begin, token_end); - - // todo : almost all language definitions use lower case to specify keywords, so shouldn't this use ::tolower ? - if (!m_languageDefinition.m_caseSensitive) - std::transform(id.begin(), id.end(), id.begin(), ::toupper); - else if (m_languageDefinition.m_keywords.count(id) != 0) - token_color = PaletteIndex::Keyword; - else if (m_languageDefinition.m_identifiers.count(id) != 0) - token_color = PaletteIndex::BuiltInType; - else if (id == "$") - token_color = PaletteIndex::GlobalVariable; - } - } else { - if ((token_color == PaletteIndex::Identifier || flags.m_bits.preprocessor) && !flags.m_bits.deactivated && !(flags.m_value & Line::inComment)) { - id.assign(token_begin, token_end); - if (m_languageDefinition.m_preprocIdentifiers.count(id) != 0) { - token_color = PaletteIndex::Directive; - token_begin -= 1; - token_length = token_end - token_begin; - token_offset -= 1; - } else if (flags.m_bits.preprocessor) { - token_color = PaletteIndex::PreprocIdentifier; - token_length = token_end - token_begin; - } - } - if (flags.m_bits.matchedBracket) { - token_color = PaletteIndex::WarningText; - token_length = token_end - token_begin; - } else if (flags.m_bits.preprocessor && !flags.m_bits.deactivated) { - token_length = token_end - token_begin; - } else if ((token_color != PaletteIndex::Directive && token_color != PaletteIndex::PreprocIdentifier) || flags.m_bits.deactivated) { - if (flags.m_bits.deactivated && flags.m_bits.preprocessor) { - 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) { - token_color = getColorIndexFromFlags(flags); - token_begin -= 1; - token_offset -= 1; - } - - auto flag = line.m_flags[token_offset]; - token_length = line.m_flags.find_first_not_of(flag, token_offset + 1); - if (token_length == std::string::npos) - token_length = line.size() - token_offset; - else - token_length -= token_offset; - - token_end = token_begin + token_length; - if (!flags.m_bits.preprocessor || flags.m_bits.deactivated) - token_color = getColorIndexFromFlags(flags); - } - } - - if (token_color != PaletteIndex::Identifier || *current.m_colorsIter == static_cast(PaletteIndex::Identifier)) { - if (token_offset + token_length >= (int32_t)line.m_colors.size()) { - auto colors = line.m_colors; - line.m_colors.resize(token_offset + token_length, static_cast(PaletteIndex::Default)); - std::copy(colors.begin(), colors.end(), line.m_colors.begin()); - } - try { - line.m_colors.replace(token_offset, token_length, token_length, static_cast(token_color)); - } catch (const std::exception &e) { - std::cerr << "Error replacing color: " << e.what() << std::endl; - return; - } - } - current = current + token_length; - } - } - line.m_colorized = true; + }; + return p; } -} -void TextEditor::colorizeInternal() { - if (isEmpty() || !m_colorizerEnabled) - return; + const TextEditor::Palette &TextEditor::getRetroBluePalette() { + const static Palette p = { + { + 0xff00ffff, // None + 0xffffff00, // Keyword + 0xff00ff00, // Number + 0xff808000, // String + 0xff808000, // Char literal + 0xffffffff, // Punctuation + 0xff008000, // Preprocessor + 0xff00ffff, // Identifier + 0xffffffff, // Known identifier + 0xffff00ff, // Preproc identifier + 0xff101010, // Global Doc Comment + 0xff202020, // Doc Comment + 0xff808080, // Comment (single line) + 0xff404040, // Comment (multi line) + 0xff004000, // Preprocessor deactivated + 0xff800000, // Background + 0xff0080ff, // Cursor + 0x80ffff00, // Selection + 0xa00000ff, // ErrorMarker + 0x80ff8000, // Breakpoint + 0xff808000, // Line number + 0x40000000, // Current line fill + 0x40808080, // Current line fill (inactive) + 0x40000000, // Current line edge + } + }; + return p; + } - if (m_updateFlags) { - auto endLine = m_lines.size(); - auto commentStartLine = endLine; - auto commentStartIndex = 0; - auto withinGlobalDocComment = false; - auto withinBlockDocComment = false; - auto withinString = false; - auto withinBlockComment = false; - auto withinNotDef = false; - auto currentLine = 0; - auto commentLength = 0; - auto matchedBracket = false; - std::string brackets = "()[]{}<>"; - std::vector ifDefs; - ifDefs.push_back(true); - m_defines.push_back("__IMHEX__"); - for (currentLine = 0; currentLine < endLine; currentLine++) { - auto &line = m_lines[currentLine]; - auto lineLength = line.size(); + std::string TextEditor::getText() const { + auto start = setCoordinates(0, 0); + auto end = setCoordinates(-1, -1); + if (start == Invalid || end == Invalid) + return ""; + return getText(Selection(start, end)); + } - if (line.m_flags.size() != lineLength) { - line.m_flags.resize(lineLength, 0); + std::vector TextEditor::getTextLines() const { + std::vector result; + + result.reserve(m_lines.size()); + + for (const auto &line: m_lines) { + std::string text = line.m_chars; + result.emplace_back(std::move(text)); + } + + return result; + } + + std::string TextEditor::getSelectedText() const { + return getText(m_state.m_selection); + } + + std::string TextEditor::getLineText(int32_t line) const { + auto sanitizedLine = setCoordinates(line, 0); + auto endLine = setCoordinates(line, -1); + if (sanitizedLine == Invalid || endLine == Invalid) + return ""; + return getText(Selection(sanitizedLine, endLine)); + } + + void TextEditor::colorize() { + m_updateFlags = true; + } + + void TextEditor::colorizeRange() { + + if (isEmpty()) + return; + + std::smatch results; + std::string id; + if (m_languageDefinition.m_tokenize == nullptr) + m_languageDefinition.m_tokenize = [](strConstIter, strConstIter, strConstIter &, strConstIter &, PaletteIndex &) { return false; }; + auto linesSize = m_lines.size(); + for (int32_t i = 0; i < linesSize; ++i) { + auto &line = m_lines[i]; + auto size = line.size(); + + if (line.m_colors.size() != size) { + line.m_colors.resize(size, 0); line.m_colorized = false; } - - auto withinComment = false; - auto withinDocComment = false; - auto withinPreproc = false; - auto firstChar = true; // there is no other non-whitespace characters in the line before - - auto setGlyphFlags = [&](int32_t 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; - flags.m_bits.deactivated = withinNotDef; - flags.m_bits.matchedBracket = 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; - } - }; - - uint64_t currentIndex = 0; - if (line.empty()) + if (line.m_colorized || line.empty()) continue; - while (currentIndex < lineLength) { - char c = line[currentIndex]; + auto last = line.end(); - matchedBracket = false; - if (MatchedBracket::s_separators.contains(c) && m_matchedBracket.isActive()) { - 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[lineCoordinateToIndex(m_matchedBracket.m_matched) - 1] == static_cast(PaletteIndex::UserDefinedType)) || - (m_matchedBracket.m_nearCursor.m_column > 0 && line.m_colors[lineCoordinateToIndex(m_matchedBracket.m_nearCursor) - 1] == static_cast(PaletteIndex::UserDefinedType)))) { - matchedBracket = true; + auto first = line.begin(); + for (auto current = first; (current - first) < size;) { + strConstIter token_begin; + strConstIter token_end; + PaletteIndex token_color = PaletteIndex::Default; + + bool hasTokenizeResult = m_languageDefinition.m_tokenize(current.m_charsIter, last.m_charsIter, token_begin, token_end, token_color); + auto token_offset = token_begin - first.m_charsIter; + + if (!hasTokenizeResult) { + // todo : remove + // printf("using regex for %.*s\n", first + 10 < last ? 10 : int32_t(last - first), first); + + for (auto &p: m_regexList) { + if (std::regex_search(first.m_charsIter, last.m_charsIter, results, p.first, std::regex_constants::match_continuous)) { + hasTokenizeResult = true; + + const auto &v = results.begin(); + token_begin = v->first; + token_end = v->second; + token_color = p.second; + break; } } } + if (!hasTokenizeResult) + current = current + 1; + else { + current = first + token_offset; + uint64_t token_length = 0; + Line::Flags flags(0); + flags.m_value = line.m_flags[token_offset]; + if (flags.m_value == 0) { + token_length = token_end - token_begin; + if (token_color == PaletteIndex::Identifier) { + id.assign(token_begin, token_end); - if (c != m_languageDefinition.m_preprocChar && !isspace(c)) - firstChar = false; - - bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); - - if (withinString) { - setGlyphFlags(currentIndex); - if (c == '\\') { - currentIndex++; - setGlyphFlags(currentIndex); - } else if (c == '\"') - withinString = false; - } else { - if (firstChar && c == m_languageDefinition.m_preprocChar && !inComment && !withinComment && !withinDocComment && !withinString) { - withinPreproc = true; - std::string directive; - auto start = currentIndex + 1; - while (start < (int32_t) line.size() && !isspace(line[start])) { - directive += line[start]; - start++; + // todo : almost all language definitions use lower case to specify keywords, so shouldn't this use ::tolower ? + if (!m_languageDefinition.m_caseSensitive) + std::transform(id.begin(), id.end(), id.begin(), ::toupper); + else if (m_languageDefinition.m_keywords.count(id) != 0) + token_color = PaletteIndex::Keyword; + else if (m_languageDefinition.m_identifiers.count(id) != 0) + token_color = PaletteIndex::BuiltInType; + else if (id == "$") + token_color = PaletteIndex::GlobalVariable; } + } else { + if ((token_color == PaletteIndex::Identifier || flags.m_bits.preprocessor) && !flags.m_bits.deactivated && !(flags.m_value & Line::inComment)) { + id.assign(token_begin, token_end); + if (m_languageDefinition.m_preprocIdentifiers.count(id) != 0) { + token_color = PaletteIndex::Directive; + token_begin -= 1; + token_length = token_end - token_begin; + token_offset -= 1; + } else if (flags.m_bits.preprocessor) { + token_color = PaletteIndex::PreprocIdentifier; + token_length = token_end - token_begin; + } + } + if (flags.m_bits.matchedBracket) { + token_color = PaletteIndex::WarningText; + token_length = token_end - token_begin; + } else if (flags.m_bits.preprocessor && !flags.m_bits.deactivated) { + token_length = token_end - token_begin; + } else if ((token_color != PaletteIndex::Directive && token_color != PaletteIndex::PreprocIdentifier) || flags.m_bits.deactivated) { + if (flags.m_bits.deactivated && flags.m_bits.preprocessor) { + 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) { + token_color = getColorIndexFromFlags(flags); + token_begin -= 1; + token_offset -= 1; + } - while (start < (int32_t) line.size() && isspace(line[start])) - start++; + auto flag = line.m_flags[token_offset]; + token_length = line.m_flags.find_first_not_of(flag, token_offset + 1); + if (token_length == std::string::npos) + token_length = line.size() - token_offset; + else + token_length -= token_offset; - if (directive == "endif" && !ifDefs.empty()) { - ifDefs.pop_back(); - withinNotDef = !ifDefs.back(); - } else { - std::string identifier; + token_end = token_begin + token_length; + if (!flags.m_bits.preprocessor || flags.m_bits.deactivated) + token_color = getColorIndexFromFlags(flags); + } + } + + if (token_color != PaletteIndex::Identifier || *current.m_colorsIter == static_cast(PaletteIndex::Identifier)) { + if (token_offset + token_length >= (int32_t) line.m_colors.size()) { + auto colors = line.m_colors; + line.m_colors.resize(token_offset + token_length, static_cast(PaletteIndex::Default)); + std::copy(colors.begin(), colors.end(), line.m_colors.begin()); + } + try { + line.m_colors.replace(token_offset, token_length, token_length, static_cast(token_color)); + } catch (const std::exception &e) { + std::cerr << "Error replacing color: " << e.what() << std::endl; + return; + } + } + current = current + token_length; + } + } + line.m_colorized = true; + } + } + + void TextEditor::colorizeInternal() { + if (isEmpty() || !m_colorizerEnabled) + return; + + if (m_updateFlags) { + auto endLine = m_lines.size(); + auto commentStartLine = endLine; + auto commentStartIndex = 0; + auto withinGlobalDocComment = false; + auto withinBlockDocComment = false; + auto withinString = false; + auto withinBlockComment = false; + auto withinNotDef = false; + auto currentLine = 0; + auto commentLength = 0; + auto matchedBracket = false; + std::string brackets = "()[]{}<>"; + + std::vector ifDefs; + ifDefs.push_back(true); + m_defines.push_back("__IMHEX__"); + for (currentLine = 0; currentLine < endLine; currentLine++) { + auto &line = m_lines[currentLine]; + auto lineLength = line.size(); + + if (line.m_flags.size() != lineLength) { + line.m_flags.resize(lineLength, 0); + line.m_colorized = false; + } + + + auto withinComment = false; + auto withinDocComment = false; + auto withinPreproc = false; + auto firstChar = true; // there is no other non-whitespace characters in the line before + + auto setGlyphFlags = [&](int32_t 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; + flags.m_bits.deactivated = withinNotDef; + flags.m_bits.matchedBracket = 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; + } + }; + + uint64_t currentIndex = 0; + if (line.empty()) + continue; + while (currentIndex < lineLength) { + + char c = line[currentIndex]; + + matchedBracket = false; + if (MatchedBracket::s_separators.contains(c) && m_matchedBracket.isActive()) { + 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[lineCoordinateToIndex(m_matchedBracket.m_matched) - 1] == static_cast(PaletteIndex::UserDefinedType)) || + (m_matchedBracket.m_nearCursor.m_column > 0 && line.m_colors[lineCoordinateToIndex(m_matchedBracket.m_nearCursor) - 1] == static_cast(PaletteIndex::UserDefinedType)))) { + matchedBracket = true; + } + } + } + + + if (c != m_languageDefinition.m_preprocChar && !isspace(c)) + firstChar = false; + + bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); + + if (withinString) { + setGlyphFlags(currentIndex); + if (c == '\\') { + currentIndex++; + setGlyphFlags(currentIndex); + } else if (c == '\"') + withinString = false; + } else { + if (firstChar && c == m_languageDefinition.m_preprocChar && !inComment && !withinComment && !withinDocComment && !withinString) { + withinPreproc = true; + std::string directive; + auto start = currentIndex + 1; while (start < (int32_t) line.size() && !isspace(line[start])) { - identifier += line[start]; + directive += line[start]; start++; } - if (directive == "define") { - if (identifier.size() > 0 && !withinNotDef && std::find(m_defines.begin(), m_defines.end(), identifier) == m_defines.end()) - 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()); - } else if (directive == "ifdef") { - if (!withinNotDef) { - bool isConditionMet = std::find(m_defines.begin(), m_defines.end(), identifier) != m_defines.end(); - 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(); - ifDefs.push_back(isConditionMet); - } else - ifDefs.push_back(false); - } - } - } - if (c == '\"' && !withinPreproc && !inComment && !withinComment && !withinDocComment) { - withinString = true; - setGlyphFlags(currentIndex); - } else { - auto pred = [](const char &a, const char &b) { return a == b; }; + while (start < (int32_t) line.size() && isspace(line[start])) + start++; - auto compareForth = [&](const std::string &a, const std::string &b) { - return !a.empty() && (currentIndex + a.size() <= b.size()) && equals(a.begin(), a.end(), b.begin() + currentIndex, b.begin() + (currentIndex + a.size()), pred); - }; - - auto compareBack = [&](const std::string &a, const std::string &b) { - return !a.empty() && currentIndex + 1 >= (int32_t) a.size() && equals(a.begin(), a.end(), b.begin() + (currentIndex + 1 - a.size()), b.begin() + (currentIndex + 1), pred); - }; - - if (!inComment && !withinComment && !withinDocComment && !withinPreproc && !withinString) { - if (compareForth(m_languageDefinition.m_docComment, line.m_chars)) { - withinDocComment = !inComment; - commentLength = 3; - } else if (compareForth(m_languageDefinition.m_singleLineComment, line.m_chars)) { - withinComment = !inComment; - commentLength = 2; + if (directive == "endif" && !ifDefs.empty()) { + ifDefs.pop_back(); + withinNotDef = !ifDefs.back(); } else { - bool isGlobalDocComment = compareForth(m_languageDefinition.m_globalDocComment, line.m_chars); - bool isBlockDocComment = compareForth(m_languageDefinition.m_blockDocComment, line.m_chars); - bool isBlockComment = compareForth(m_languageDefinition.m_commentStart, line.m_chars); - if (isGlobalDocComment || isBlockDocComment || isBlockComment) { - commentStartLine = currentLine; - commentStartIndex = currentIndex; - if (currentIndex < line.size() - 4 && isBlockComment && - line.m_chars[currentIndex + 2] == '*' && - line.m_chars[currentIndex + 3] == '/') { - withinBlockComment = true; - commentLength = 2; - } else if (isGlobalDocComment) { - withinGlobalDocComment = true; - commentLength = 3; - } else if (isBlockDocComment) { - withinBlockDocComment = true; - commentLength = 3; - } else { - withinBlockComment = true; - commentLength = 2; - } + std::string identifier; + while (start < (int32_t) line.size() && !isspace(line[start])) { + identifier += line[start]; + start++; + } + if (directive == "define") { + if (identifier.size() > 0 && !withinNotDef && std::find(m_defines.begin(), m_defines.end(), identifier) == m_defines.end()) + 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()); + } else if (directive == "ifdef") { + if (!withinNotDef) { + bool isConditionMet = std::find(m_defines.begin(), m_defines.end(), identifier) != m_defines.end(); + 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(); + ifDefs.push_back(isConditionMet); + } else + ifDefs.push_back(false); } } - inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); } - setGlyphFlags(currentIndex); - if (compareBack(m_languageDefinition.m_commentEnd, line.m_chars) && ((commentStartLine != currentLine) || (commentStartIndex + commentLength < currentIndex))) { - withinBlockComment = false; - withinBlockDocComment = false; - withinGlobalDocComment = false; - commentStartLine = endLine; - commentStartIndex = 0; - commentLength = 0; + if (c == '\"' && !withinPreproc && !inComment && !withinComment && !withinDocComment) { + withinString = true; + setGlyphFlags(currentIndex); + } else { + auto pred = [](const char &a, const char &b) { return a == b; }; + + auto compareForth = [&](const std::string &a, const std::string &b) { + return !a.empty() && (currentIndex + a.size() <= b.size()) && equals(a.begin(), a.end(), b.begin() + currentIndex, b.begin() + (currentIndex + a.size()), pred); + }; + + auto compareBack = [&](const std::string &a, const std::string &b) { + return !a.empty() && currentIndex + 1 >= (int32_t) a.size() && equals(a.begin(), a.end(), b.begin() + (currentIndex + 1 - a.size()), b.begin() + (currentIndex + 1), pred); + }; + + if (!inComment && !withinComment && !withinDocComment && !withinPreproc && !withinString) { + if (compareForth(m_languageDefinition.m_docComment, line.m_chars)) { + withinDocComment = !inComment; + commentLength = 3; + } else if (compareForth(m_languageDefinition.m_singleLineComment, line.m_chars)) { + withinComment = !inComment; + commentLength = 2; + } else { + bool isGlobalDocComment = compareForth(m_languageDefinition.m_globalDocComment, line.m_chars); + bool isBlockDocComment = compareForth(m_languageDefinition.m_blockDocComment, line.m_chars); + bool isBlockComment = compareForth(m_languageDefinition.m_commentStart, line.m_chars); + if (isGlobalDocComment || isBlockDocComment || isBlockComment) { + commentStartLine = currentLine; + commentStartIndex = currentIndex; + if (currentIndex < line.size() - 4 && isBlockComment && + line.m_chars[currentIndex + 2] == '*' && + line.m_chars[currentIndex + 3] == '/') { + withinBlockComment = true; + commentLength = 2; + } else if (isGlobalDocComment) { + withinGlobalDocComment = true; + commentLength = 3; + } else if (isBlockDocComment) { + withinBlockDocComment = true; + commentLength = 3; + } else { + withinBlockComment = true; + commentLength = 2; + } + } + } + inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); + } + setGlyphFlags(currentIndex); + + if (compareBack(m_languageDefinition.m_commentEnd, line.m_chars) && ((commentStartLine != currentLine) || (commentStartIndex + commentLength < currentIndex))) { + withinBlockComment = false; + withinBlockDocComment = false; + withinGlobalDocComment = false; + commentStartLine = endLine; + commentStartIndex = 0; + commentLength = 0; + } } } - } - if (currentIndex < line.size()) { - Line::Flags flags(0); - flags.m_value = m_lines[currentLine].m_flags[currentIndex]; - flags.m_bits.preprocessor = withinPreproc; - m_lines[currentLine].m_flags[currentIndex] = flags.m_value; - } - auto utf8CharLen = utf8CharLength(c); - if (utf8CharLen > 1) { - Line::Flags flags(0); - flags.m_value = m_lines[currentLine].m_flags[currentIndex]; - for (int32_t j = 1; j < utf8CharLen; j++) { - currentIndex++; + if (currentIndex < line.size()) { + Line::Flags flags(0); + flags.m_value = m_lines[currentLine].m_flags[currentIndex]; + flags.m_bits.preprocessor = withinPreproc; m_lines[currentLine].m_flags[currentIndex] = flags.m_value; } + auto utf8CharLen = utf8CharLength(c); + if (utf8CharLen > 1) { + Line::Flags flags(0); + flags.m_value = m_lines[currentLine].m_flags[currentIndex]; + for (int32_t j = 1; j < utf8CharLen; j++) { + currentIndex++; + m_lines[currentLine].m_flags[currentIndex] = flags.m_value; + } + } + currentIndex++; } - currentIndex++; + withinNotDef = !ifDefs.back(); } - withinNotDef = !ifDefs.back(); + m_defines.clear(); } - m_defines.clear(); - } - colorizeRange(); -} - -float TextEditor::textDistanceToLineStart(const Coordinates &aFrom) const { - auto &line = m_lines[aFrom.m_line]; - int32_t colIndex = lineCoordinateToIndex(aFrom); - auto substr = line.m_chars.substr(0, colIndex); - return ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, substr.c_str(), nullptr, nullptr).x; -} - -void TextEditor::setScrollY() { - if (!m_withinRender) { - m_setScrollY = true; - return; - } else { - m_setScrollY = false; - auto scrollY = ImGui::GetScrollY(); - ImGui::SetScrollY(std::clamp(scrollY + m_scrollYIncrement, 0.0f, ImGui::GetScrollMaxY())); - } -} - -void TextEditor::setTopLine() { - if (!m_withinRender) { - m_setTopLine = true; - return; - } else { - m_setTopLine = false; - ImGui::SetScrollY(m_topLine * m_charAdvance.y); - ensureCursorVisible(); - } -} - -void TextEditor::ensureCursorVisible() { - if (!m_withinRender) { - m_scrollToCursor = true; - return; + colorizeRange(); } - auto scrollBarSize = ImGui::GetStyle().ScrollbarSize; - float scrollX = ImGui::GetScrollX(); - float scrollY = ImGui::GetScrollY(); - - auto windowPadding = ImGui::GetStyle().FramePadding * 2.0f; - - auto height = ImGui::GetWindowHeight() - m_topMargin - scrollBarSize - m_charAdvance.y; - auto width = ImGui::GetWindowWidth() - windowPadding.x - scrollBarSize; - - auto top = (int32_t)rint((m_topMargin > scrollY ? m_topMargin - scrollY : scrollY) / m_charAdvance.y); - auto bottom = top + (int32_t)rint(height / m_charAdvance.y); - - auto left = (int32_t)rint(scrollX / m_charAdvance.x); - auto right = left + (int32_t)rint(width / m_charAdvance.x); - - auto pos = setCoordinates(m_state.m_cursorPosition); - pos.m_column = (int32_t)rint(textDistanceToLineStart(pos) / m_charAdvance.x); - - bool mScrollToCursorX = true; - bool mScrollToCursorY = true; - - 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) { - m_scrollToCursor = false; - return; + float TextEditor::textDistanceToLineStart(const Coordinates &aFrom) const { + auto &line = m_lines[aFrom.m_line]; + int32_t colIndex = lineCoordinateToIndex(aFrom); + auto substr = line.m_chars.substr(0, colIndex); + return ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, substr.c_str(), nullptr, nullptr).x; } - if (mScrollToCursorY) { - if (pos.m_line < top) - ImGui::SetScrollY(std::max(0.0f, pos.m_line * m_charAdvance.y)); - if (pos.m_line > bottom) - ImGui::SetScrollY(std::max(0.0f, pos.m_line * m_charAdvance.y - height)); - } - if (mScrollToCursorX) { - if (pos.m_column < left) - ImGui::SetScrollX(std::max(0.0f, pos.m_column * m_charAdvance.x )); - if (pos.m_column > right) - ImGui::SetScrollX(std::max(0.0f, pos.m_column * m_charAdvance.x - width)); - } - m_oldTopMargin = m_topMargin; -} - -float TextEditor::getPageSize() const { - auto height = ImGui::GetCurrentWindow()->InnerClipRect.GetHeight(); - return height / m_charAdvance.y; -} - -void TextEditor::resetCursorBlinkTime() { - m_startTime = ImGui::GetTime() * 1000 - s_cursorBlinkOnTime; -} - -TextEditor::UndoRecord::UndoRecord( - const std::string &added, - const TextEditor::Selection addedSelection, - const std::string &removed, - const TextEditor::Selection 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) {} - -void TextEditor::UndoRecord::undo(TextEditor *editor) { - if (!m_added.empty()) { - editor->deleteRange(m_addedSelection); - editor->colorize(); + void TextEditor::setScrollY() { + if (!m_withinRender) { + m_setScrollY = true; + return; + } else { + m_setScrollY = false; + auto scrollY = ImGui::GetScrollY(); + ImGui::SetScrollY(std::clamp(scrollY + m_scrollYIncrement, 0.0f, ImGui::GetScrollMaxY())); + } } - if (!m_removed.empty()) { - auto start = m_removedSelection.m_start; - editor->insertTextAt(start, m_removed.c_str()); - editor->colorize(); + void TextEditor::setTopLine() { + if (!m_withinRender) { + m_setTopLine = true; + return; + } else { + m_setTopLine = false; + ImGui::SetScrollY(m_topLine * m_charAdvance.y); + ensureCursorVisible(); + } } - editor->m_state = m_before; - editor->ensureCursorVisible(); -} + void TextEditor::ensureCursorVisible() { + if (!m_withinRender) { + m_scrollToCursor = true; + return; + } -void TextEditor::UndoRecord::redo(TextEditor *editor) { - if (!m_removed.empty()) { - editor->deleteRange(m_removedSelection); - editor->colorize(); + auto scrollBarSize = ImGui::GetStyle().ScrollbarSize; + float scrollX = ImGui::GetScrollX(); + float scrollY = ImGui::GetScrollY(); + + auto windowPadding = ImGui::GetStyle().FramePadding * 2.0f; + + auto height = ImGui::GetWindowHeight() - m_topMargin - scrollBarSize - m_charAdvance.y; + auto width = ImGui::GetWindowWidth() - windowPadding.x - scrollBarSize; + + auto top = (int32_t) rint((m_topMargin > scrollY ? m_topMargin - scrollY : scrollY) / m_charAdvance.y); + auto bottom = top + (int32_t) rint(height / m_charAdvance.y); + + auto left = (int32_t) rint(scrollX / m_charAdvance.x); + auto right = left + (int32_t) rint(width / m_charAdvance.x); + + auto pos = setCoordinates(m_state.m_cursorPosition); + pos.m_column = (int32_t) rint(textDistanceToLineStart(pos) / m_charAdvance.x); + + bool mScrollToCursorX = true; + bool mScrollToCursorY = true; + + 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) { + m_scrollToCursor = false; + return; + } + + if (mScrollToCursorY) { + if (pos.m_line < top) + ImGui::SetScrollY(std::max(0.0f, pos.m_line * m_charAdvance.y)); + if (pos.m_line > bottom) + ImGui::SetScrollY(std::max(0.0f, pos.m_line * m_charAdvance.y - height)); + } + if (mScrollToCursorX) { + if (pos.m_column < left) + ImGui::SetScrollX(std::max(0.0f, pos.m_column * m_charAdvance.x)); + if (pos.m_column > right) + ImGui::SetScrollX(std::max(0.0f, pos.m_column * m_charAdvance.x - width)); + } + m_oldTopMargin = m_topMargin; } - if (!m_added.empty()) { - auto start = m_addedSelection.m_start; - editor->insertTextAt(start, m_added.c_str()); - editor->colorize(); + float TextEditor::getPageSize() const { + auto height = ImGui::GetCurrentWindow()->InnerClipRect.GetHeight(); + return height / m_charAdvance.y; } - editor->m_state = m_after; - editor->ensureCursorVisible(); + void TextEditor::resetCursorBlinkTime() { + m_startTime = ImGui::GetTime() * 1000 - s_cursorBlinkOnTime; + } -} + TextEditor::UndoRecord::UndoRecord( + const std::string &added, + const TextEditor::Selection addedSelection, + const std::string &removed, + const TextEditor::Selection 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) {} -bool tokenizeCStyleString(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end) { - auto p = in_begin; + void TextEditor::UndoRecord::undo(TextEditor *editor) { + if (!m_added.empty()) { + editor->deleteRange(m_addedSelection); + editor->colorize(); + } - if (*p == '"') { - p++; + if (!m_removed.empty()) { + auto start = m_removedSelection.m_start; + editor->insertTextAt(start, m_removed.c_str()); + editor->colorize(); + } - while (p < in_end) { - // handle end of string - if (*p == '"') { + editor->m_state = m_before; + editor->ensureCursorVisible(); + } + + void TextEditor::UndoRecord::redo(TextEditor *editor) { + if (!m_removed.empty()) { + editor->deleteRange(m_removedSelection); + editor->colorize(); + } + + if (!m_added.empty()) { + auto start = m_addedSelection.m_start; + editor->insertTextAt(start, m_added.c_str()); + editor->colorize(); + } + + editor->m_state = m_after; + editor->ensureCursorVisible(); + + } + + bool tokenizeCStyleString(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end) { + auto p = in_begin; + + if (*p == '"') { + p++; + + while (p < in_end) { + // handle end of string + if (*p == '"') { + out_begin = in_begin; + out_end = p + 1; + return true; + } + + // handle escape character for " + if (*p == '\\' && p + 1 < in_end && p[1] == '\\') + p++; + else if (*p == '\\' && p + 1 < in_end && p[1] == '"') + p++; + + p++; + } + } + + return false; + } + + bool tokenizeCStyleCharacterLiteral(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end) { + auto p = in_begin; + + if (*p == '\'') { + p++; + + // handle escape characters + if (p < in_end && *p == '\\') + p++; + + if (p < in_end) + p++; + + // handle end of character literal + if (p < in_end && *p == '\'') { out_begin = in_begin; - out_end = p + 1; + out_end = p + 1; return true; } - - // handle escape character for " - if (*p == '\\' && p + 1 < in_end && p[1] == '\\') - p++; - else if (*p == '\\' && p + 1 < in_end && p[1] == '"') - p++; - - p++; } + + return false; } - return false; -} + bool tokenizeCStyleIdentifier(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end) { + auto p = in_begin; -bool tokenizeCStyleCharacterLiteral(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end) { - auto p = in_begin; - - if (*p == '\'') { - p++; - - // handle escape characters - if (p < in_end && *p == '\\') + if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_' || *p == '$') { p++; - if (p < in_end) - p++; + while ((p < in_end) && ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_')) + p++; - // handle end of character literal - if (p < in_end && *p == '\'') { out_begin = in_begin; - out_end = p + 1; + out_end = p; return true; } + + return false; } - return false; -} + bool tokenizeCStyleNumber(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end) { + auto p = in_begin; -bool tokenizeCStyleIdentifier(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end) { - auto p = in_begin; + const bool startsWithNumber = *p >= '0' && *p <= '9'; + + if (!startsWithNumber) + return false; - if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_' || *p == '$') { p++; - while ((p < in_end) && ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_')) + bool hasNumber = startsWithNumber; + + while (p < in_end && (*p >= '0' && *p <= '9')) { + hasNumber = true; + p++; + } + + if (!hasNumber) + return false; + + bool isFloat = false; + bool isHex = false; + bool isBinary = false; + + if (p < in_end) { + if (*p == '.') { + isFloat = true; + + p++; + + while (p < in_end && (*p >= '0' && *p <= '9')) + p++; + } else if (*p == 'x' || *p == 'X') { + // hex formatted integer of the type 0xef80 + + isHex = true; + + p++; + + while (p < in_end && ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F') || *p == '.' || *p == 'p' || *p == 'P')) + p++; + } else if (*p == 'b' || *p == 'B') { + // binary formatted integer of the type 0b01011101 + + isBinary = true; + + p++; + + while (p < in_end && (*p >= '0' && *p <= '1')) + p++; + } + } + + if (!isHex && !isBinary) { + // floating point exponent + if (p < in_end && (*p == 'e' || *p == 'E')) { + isFloat = true; + + p++; + + if (p < in_end && (*p == '+' || *p == '-')) + p++; + + bool hasDigits = false; + + while (p < in_end && (*p >= '0' && *p <= '9')) { + hasDigits = true; + + p++; + } + + if (!hasDigits) + return false; + } + + // single and double precision floating point type + if (p < in_end && (*p == 'f' || *p == 'F' || *p == 'd' || *p == 'D')) + p++; + } + + if (!isFloat) { + // integer size type + while (p < in_end && (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L')) + p++; + } out_begin = in_begin; - out_end = p; + out_end = p; return true; } - return false; -} + bool tokenizeCStyleOperator(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end) { + (void) in_end; -bool tokenizeCStyleNumber(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end) { - auto p = in_begin; - - const bool startsWithNumber = *p >= '0' && *p <= '9'; - - if (!startsWithNumber) - return false; - - p++; - - bool hasNumber = startsWithNumber; - - while (p < in_end && (*p >= '0' && *p <= '9')) { - hasNumber = true; - - p++; - } - - if (!hasNumber) - return false; - - bool isFloat = false; - bool isHex = false; - bool isBinary = false; - - if (p < in_end) { - if (*p == '.') { - isFloat = true; - - p++; - - while (p < in_end && (*p >= '0' && *p <= '9')) - p++; - } else if (*p == 'x' || *p == 'X') { - // hex formatted integer of the type 0xef80 - - isHex = true; - - p++; - - while (p < in_end && ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F') || *p == '.' || *p == 'p' || *p == 'P')) - p++; - } else if (*p == 'b' || *p == 'B') { - // binary formatted integer of the type 0b01011101 - - isBinary = true; - - p++; - - while (p < in_end && (*p >= '0' && *p <= '1')) - p++; + switch (*in_begin) { + case '!': + case '%': + case '^': + case '&': + case '*': + case '-': + case '+': + case '=': + case '~': + case '|': + case '<': + case '>': + case '?': + case ':': + case '/': + case '@': + out_begin = in_begin; + out_end = in_begin + 1; + return true; } + + return false; } - if (!isHex && !isBinary) { - // floating point exponent - if (p < in_end && (*p == 'e' || *p == 'E')) { - isFloat = true; + bool tokenizeCStyleSeparator(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end) { + (void) in_end; - p++; + switch (*in_begin) { + case '[': + case ']': + case '{': + case '}': + case '(': + case ')': + case ';': + case ',': + case '.': + out_begin = in_begin; + out_end = in_begin + 1; + return true; + } - if (p < in_end && (*p == '+' || *p == '-')) - p++; + return false; + } - bool hasDigits = false; + const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::CPlusPlus() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const cppKeywords[] = { + "alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit", "atomic_noexcept", + "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "char16_t", "char32_t", + "class", "compl", "concept", "const", "constexpr", "const_cast", "continue", "decltype", "default", + "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", + "float", "for", "friend", "goto", "if", "import", "inline", "int", "long", "module", "mutable", + "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", + "protected", "public", "register", "reinterpret_cast", "requires", "return", "short", "signed", + "sizeof", "static", "static_assert", "static_cast", "struct", "switch", "synchronized", "template", + "this", "thread_local", "throw", "true", "try", "typedef", "typeid", "typename", "union", + "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq" + }; + for (auto &k: cppKeywords) + langDef.m_keywords.insert(k); - while (p < in_end && (*p >= '0' && *p <= '9')) { - hasDigits = true; - - p++; + static const char *const identifiers[] = { + "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", + "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", + "isdigit", "isgraph", "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", + "modf", "pow", "printf", "sprintf", "snprintf", "putchar", "putenv", "puts", "rand", "remove", + "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper", + "std", "string", "vector", "map", "unordered_map", "set", "unordered_set", "min", "max" + }; + for (auto &k: identifiers) { + Identifier id; + id.m_declaration = "Built-in function"; + langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); } - if (!hasDigits) - return false; + langDef.m_tokenize = [](strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end, PaletteIndex &paletteIndex) -> bool { + paletteIndex = PaletteIndex::Max; + + while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin)) + in_begin++; + + if (in_begin == in_end) { + out_begin = in_end; + out_end = in_end; + paletteIndex = PaletteIndex::Default; + } else if (tokenizeCStyleString(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::StringLiteral; + else if (tokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::CharLiteral; + else if (tokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Identifier; + else if (tokenizeCStyleNumber(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::NumericLiteral; + else if (tokenizeCStyleSeparator(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Separator; + else if (tokenizeCStyleOperator(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Operator; + + return paletteIndex != PaletteIndex::Max; + }; + + langDef.m_commentStart = "/*"; + langDef.m_commentEnd = "*/"; + langDef.m_singleLineComment = "//"; + + langDef.m_caseSensitive = true; + langDef.m_autoIndentation = true; + + langDef.m_name = "C++"; + + inited = true; } - - // single and double precision floating point type - if (p < in_end && (*p == 'f' || *p == 'F' || *p == 'd' || *p == 'D')) - p++; + return langDef; } - if (!isFloat) { - // integer size type - while (p < in_end && (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L')) - p++; - } + const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::HLSL() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const keywords[] = { + "AppendStructuredBuffer", + "asm", + "asm_fragment", + "BlendState", + "bool", + "break", + "Buffer", + "ByteAddressBuffer", + "case", + "cbuffer", + "centroid", + "class", + "column_major", + "compile", + "compile_fragment", + "CompileShader", + "const", + "continue", + "ComputeShader", + "ConsumeStructuredBuffer", + "default", + "DepthStencilState", + "DepthStencilView", + "discard", + "do", + "double", + "DomainShader", + "dword", + "else", + "export", + "extern", + "false", + "float", + "for", + "fxgroup", + "GeometryShader", + "groupshared", + "half", + "Hullshader", + "if", + "in", + "inline", + "inout", + "InputPatch", + "int", + "interface", + "line", + "lineadj", + "linear", + "LineStream", + "matrix", + "min16float", + "min10float", + "min16int", + "min12int", + "min16uint", + "namespace", + "nointerpolation", + "noperspective", + "NULL", + "out", + "OutputPatch", + "packoffset", + "pass", + "pixelfragment", + "PixelShader", + "point", + "PointStream", + "precise", + "RasterizerState", + "RenderTargetView", + "return", + "register", + "row_major", + "RWBuffer", + "RWByteAddressBuffer", + "RWStructuredBuffer", + "RWTexture1D", + "RWTexture1DArray", + "RWTexture2D", + "RWTexture2DArray", + "RWTexture3D", + "sample", + "sampler", + "SamplerState", + "SamplerComparisonState", + "shared", + "snorm", + "stateblock", + "stateblock_state", + "static", + "string", + "struct", + "switch", + "StructuredBuffer", + "tbuffer", + "technique", + "technique10", + "technique11", + "texture", + "Texture1D", + "Texture1DArray", + "Texture2D", + "Texture2DArray", + "Texture2DMS", + "Texture2DMSArray", + "Texture3D", + "TextureCube", + "TextureCubeArray", + "true", + "typedef", + "triangle", + "triangleadj", + "TriangleStream", + "uint", + "uniform", + "unorm", + "unsigned", + "vector", + "vertexfragment", + "VertexShader", + "void", + "volatile", + "while", + "bool1", + "bool2", + "bool3", + "bool4", + "double1", + "double2", + "double3", + "double4", + "float1", + "float2", + "float3", + "float4", + "int1", + "int2", + "int3", + "int4", + "in", + "out", + "inout", + "uint1", + "uint2", + "uint3", + "uint4", + "dword1", + "dword2", + "dword3", + "dword4", + "half1", + "half2", + "half3", + "half4", + "float1x1", + "float2x1", + "float3x1", + "float4x1", + "float1x2", + "float2x2", + "float3x2", + "float4x2", + "float1x3", + "float2x3", + "float3x3", + "float4x3", + "float1x4", + "float2x4", + "float3x4", + "float4x4", + "half1x1", + "half2x1", + "half3x1", + "half4x1", + "half1x2", + "half2x2", + "half3x2", + "half4x2", + "half1x3", + "half2x3", + "half3x3", + "half4x3", + "half1x4", + "half2x4", + "half3x4", + "half4x4", + }; + for (auto &k: keywords) + langDef.m_keywords.insert(k); - out_begin = in_begin; - out_end = p; - return true; -} + static const char *const identifiers[] = { + "abort", "abs", "acos", "all", "AllMemoryBarrier", "AllMemoryBarrierWithGroupSync", "any", + "asdouble", "asfloat", "asin", "asint", "asint", "asuint", "asuint", "atan", "atan2", "ceil", + "CheckAccessFullyMapped", "clamp", "clip", "cos", "cosh", "countbits", "cross", "D3DCOLORtoUBYTE4", + "ddx", "ddx_coarse", "ddx_fine", "ddy", "ddy_coarse", "ddy_fine", "degrees", "determinant", + "DeviceMemoryBarrier", "DeviceMemoryBarrierWithGroupSync", "distance", "dot", "dst", "errorf", + "EvaluateAttributeAtCentroid", "EvaluateAttributeAtSample", "EvaluateAttributeSnapped", "exp", + "exp2", "f16tof32", "f32tof16", "faceforward", "firstbithigh", "firstbitlow", "floor", "fma", + "fmod", "frac", "frexp", "fwidth", "GetRenderTargetSampleCount", "GetRenderTargetSamplePosition", + "GroupMemoryBarrier", "GroupMemoryBarrierWithGroupSync", "InterlockedAdd", "InterlockedAnd", + "InterlockedCompareExchange", "InterlockedCompareStore", "InterlockedExchange", "InterlockedMax", + "InterlockedMin", "InterlockedOr", "InterlockedXor", "isfinite", "isinf", "isnan", "ldexp", + "length", "lerp", "lit", "log", "log10", "log2", "mad", "max", "min", "modf", "msad4", "mul", + "noise", "normalize", "pow", "printf", "Process2DQuadTessFactorsAvg", "Process2DQuadTessFactorsMax", + "Process2DQuadTessFactorsMin", "ProcessIsolineTessFactors", "ProcessQuadTessFactorsAvg", + "ProcessQuadTessFactorsMax", "ProcessQuadTessFactorsMin", "ProcessTriTessFactorsAvg", + "ProcessTriTessFactorsMax", "ProcessTriTessFactorsMin", "radians", "rcp", "reflect", "refract", + "reversebits", "round", "rsqrt", "saturate", "sign", "sin", "sincos", "sinh", "smoothstep", "sqrt", + "step", "tan", "tanh", "tex1D", "tex1D", "tex1Dbias", "tex1Dgrad", "tex1Dlod", "tex1Dproj", "tex2D", + "tex2D", "tex2Dbias", "tex2Dgrad", "tex2Dlod", "tex2Dproj", "tex3D", "tex3D", "tex3Dbias", + "tex3Dgrad", "tex3Dlod", "tex3Dproj", "texCUBE", "texCUBE", "texCUBEbias", "texCUBEgrad", + "texCUBElod", "texCUBEproj", "transpose", "trunc" + }; + for (auto &k: identifiers) { + Identifier id; + id.m_declaration = "Built-in function"; + langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); + } -bool tokenizeCStyleOperator(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end) { - (void)in_end; + langDef.m_tokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Directive)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::StringLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\!\\%\\^\\&\\*\\-\\+\\=\\~\\|\\<\\>\\?\\/]", PaletteIndex::Operator)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\(\\)\\;\\,\\.]", PaletteIndex::Separator)); + langDef.m_commentStart = "/*"; + langDef.m_commentEnd = "*/"; + langDef.m_singleLineComment = "//"; - switch (*in_begin) { - case '!': - case '%': - case '^': - case '&': - case '*': - case '-': - case '+': - case '=': - case '~': - case '|': - case '<': - case '>': - case '?': - case ':': - case '/': - case '@': - out_begin = in_begin; - out_end = in_begin + 1; - return true; - } + langDef.m_caseSensitive = true; + langDef.m_autoIndentation = true; - return false; -} + langDef.m_name = "HLSL"; -bool tokenizeCStyleSeparator(strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end) { - (void)in_end; - - switch (*in_begin) { - case '[': - case ']': - case '{': - case '}': - case '(': - case ')': - case ';': - case ',': - case '.': - out_begin = in_begin; - out_end = in_begin + 1; - return true; - } - - return false; -} - -const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::CPlusPlus() { - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) { - static const char *const cppKeywords[] = { - "alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit", "atomic_noexcept", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class", "compl", "concept", "const", "constexpr", "const_cast", "continue", "decltype", "default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float", "for", "friend", "goto", "if", "import", "inline", "int", "long", "module", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", "public", "register", "reinterpret_cast", "requires", "return", "short", "signed", "sizeof", "static", "static_assert", "static_cast", "struct", "switch", "synchronized", "template", "this", "thread_local", "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq" - }; - for (auto &k : cppKeywords) - langDef.m_keywords.insert(k); - - static const char *const identifiers[] = { - "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "printf", "sprintf", "snprintf", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper", "std", "string", "vector", "map", "unordered_map", "set", "unordered_set", "min", "max" - }; - for (auto &k : identifiers) { - Identifier id; - id.m_declaration = "Built-in function"; - langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); + inited = true; } - - langDef.m_tokenize = [](strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end, PaletteIndex &paletteIndex) -> bool { - paletteIndex = PaletteIndex::Max; - - while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin)) - in_begin++; - - if (in_begin == in_end) { - out_begin = in_end; - out_end = in_end; - paletteIndex = PaletteIndex::Default; - } else if (tokenizeCStyleString(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::StringLiteral; - else if (tokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::CharLiteral; - else if (tokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::Identifier; - else if (tokenizeCStyleNumber(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::NumericLiteral; - else if (tokenizeCStyleSeparator(in_begin, in_end, out_begin, out_end)) - paletteIndex= PaletteIndex::Separator; - else if (tokenizeCStyleOperator(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::Operator; - - return paletteIndex != PaletteIndex::Max; - }; - - langDef.m_commentStart = "/*"; - langDef.m_commentEnd = "*/"; - langDef.m_singleLineComment = "//"; - - langDef.m_caseSensitive = true; - langDef.m_autoIndentation = true; - - langDef.m_name = "C++"; - - inited = true; + return langDef; } - return langDef; -} -const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::HLSL() { - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) { - static const char *const keywords[] = { - "AppendStructuredBuffer", - "asm", - "asm_fragment", - "BlendState", - "bool", - "break", - "Buffer", - "ByteAddressBuffer", - "case", - "cbuffer", - "centroid", - "class", - "column_major", - "compile", - "compile_fragment", - "CompileShader", - "const", - "continue", - "ComputeShader", - "ConsumeStructuredBuffer", - "default", - "DepthStencilState", - "DepthStencilView", - "discard", - "do", - "double", - "DomainShader", - "dword", - "else", - "export", - "extern", - "false", - "float", - "for", - "fxgroup", - "GeometryShader", - "groupshared", - "half", - "Hullshader", - "if", - "in", - "inline", - "inout", - "InputPatch", - "int", - "interface", - "line", - "lineadj", - "linear", - "LineStream", - "matrix", - "min16float", - "min10float", - "min16int", - "min12int", - "min16uint", - "namespace", - "nointerpolation", - "noperspective", - "NULL", - "out", - "OutputPatch", - "packoffset", - "pass", - "pixelfragment", - "PixelShader", - "point", - "PointStream", - "precise", - "RasterizerState", - "RenderTargetView", - "return", - "register", - "row_major", - "RWBuffer", - "RWByteAddressBuffer", - "RWStructuredBuffer", - "RWTexture1D", - "RWTexture1DArray", - "RWTexture2D", - "RWTexture2DArray", - "RWTexture3D", - "sample", - "sampler", - "SamplerState", - "SamplerComparisonState", - "shared", - "snorm", - "stateblock", - "stateblock_state", - "static", - "string", - "struct", - "switch", - "StructuredBuffer", - "tbuffer", - "technique", - "technique10", - "technique11", - "texture", - "Texture1D", - "Texture1DArray", - "Texture2D", - "Texture2DArray", - "Texture2DMS", - "Texture2DMSArray", - "Texture3D", - "TextureCube", - "TextureCubeArray", - "true", - "typedef", - "triangle", - "triangleadj", - "TriangleStream", - "uint", - "uniform", - "unorm", - "unsigned", - "vector", - "vertexfragment", - "VertexShader", - "void", - "volatile", - "while", - "bool1", - "bool2", - "bool3", - "bool4", - "double1", - "double2", - "double3", - "double4", - "float1", - "float2", - "float3", - "float4", - "int1", - "int2", - "int3", - "int4", - "in", - "out", - "inout", - "uint1", - "uint2", - "uint3", - "uint4", - "dword1", - "dword2", - "dword3", - "dword4", - "half1", - "half2", - "half3", - "half4", - "float1x1", - "float2x1", - "float3x1", - "float4x1", - "float1x2", - "float2x2", - "float3x2", - "float4x2", - "float1x3", - "float2x3", - "float3x3", - "float4x3", - "float1x4", - "float2x4", - "float3x4", - "float4x4", - "half1x1", - "half2x1", - "half3x1", - "half4x1", - "half1x2", - "half2x2", - "half3x2", - "half4x2", - "half1x3", - "half2x3", - "half3x3", - "half4x3", - "half1x4", - "half2x4", - "half3x4", - "half4x4", - }; - for (auto &k : keywords) - langDef.m_keywords.insert(k); + const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::GLSL() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const keywords[] = { + "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", + "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", + "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", + "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", + "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local" + }; + for (auto &k: keywords) + langDef.m_keywords.insert(k); - static const char *const identifiers[] = { - "abort", "abs", "acos", "all", "AllMemoryBarrier", "AllMemoryBarrierWithGroupSync", "any", "asdouble", "asfloat", "asin", "asint", "asint", "asuint", "asuint", "atan", "atan2", "ceil", "CheckAccessFullyMapped", "clamp", "clip", "cos", "cosh", "countbits", "cross", "D3DCOLORtoUBYTE4", "ddx", "ddx_coarse", "ddx_fine", "ddy", "ddy_coarse", "ddy_fine", "degrees", "determinant", "DeviceMemoryBarrier", "DeviceMemoryBarrierWithGroupSync", "distance", "dot", "dst", "errorf", "EvaluateAttributeAtCentroid", "EvaluateAttributeAtSample", "EvaluateAttributeSnapped", "exp", "exp2", "f16tof32", "f32tof16", "faceforward", "firstbithigh", "firstbitlow", "floor", "fma", "fmod", "frac", "frexp", "fwidth", "GetRenderTargetSampleCount", "GetRenderTargetSamplePosition", "GroupMemoryBarrier", "GroupMemoryBarrierWithGroupSync", "InterlockedAdd", "InterlockedAnd", "InterlockedCompareExchange", "InterlockedCompareStore", "InterlockedExchange", "InterlockedMax", "InterlockedMin", "InterlockedOr", "InterlockedXor", "isfinite", "isinf", "isnan", "ldexp", "length", "lerp", "lit", "log", "log10", "log2", "mad", "max", "min", "modf", "msad4", "mul", "noise", "normalize", "pow", "printf", "Process2DQuadTessFactorsAvg", "Process2DQuadTessFactorsMax", "Process2DQuadTessFactorsMin", "ProcessIsolineTessFactors", "ProcessQuadTessFactorsAvg", "ProcessQuadTessFactorsMax", "ProcessQuadTessFactorsMin", "ProcessTriTessFactorsAvg", "ProcessTriTessFactorsMax", "ProcessTriTessFactorsMin", "radians", "rcp", "reflect", "refract", "reversebits", "round", "rsqrt", "saturate", "sign", "sin", "sincos", "sinh", "smoothstep", "sqrt", "step", "tan", "tanh", "tex1D", "tex1D", "tex1Dbias", "tex1Dgrad", "tex1Dlod", "tex1Dproj", "tex2D", "tex2D", "tex2Dbias", "tex2Dgrad", "tex2Dlod", "tex2Dproj", "tex3D", "tex3D", "tex3Dbias", "tex3Dgrad", "tex3Dlod", "tex3Dproj", "texCUBE", "texCUBE", "texCUBEbias", "texCUBEgrad", "texCUBElod", "texCUBEproj", "transpose", "trunc" - }; - for (auto &k : identifiers) { - Identifier id; - id.m_declaration = "Built-in function"; - langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); + static const char *const identifiers[] = { + "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", + "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", + "isdigit", "isgraph", "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", + "modf", "pow", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", + "strcat", "strcmp", "strerror", "time", "tolower", "toupper" + }; + for (auto &k: identifiers) { + Identifier id; + id.m_declaration = "Built-in function"; + langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.m_tokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Directive)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::StringLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\!\\%\\^\\&\\*\\-\\+\\=\\~\\|\\<\\>\\?\\/]", PaletteIndex::Operator)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\(\\)\\;\\,\\.]", PaletteIndex::Separator)); + langDef.m_commentStart = "/*"; + langDef.m_commentEnd = "*/"; + langDef.m_singleLineComment = "//"; + + langDef.m_caseSensitive = true; + langDef.m_autoIndentation = true; + + langDef.m_name = "GLSL"; + + inited = true; } - - langDef.m_tokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Directive)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::StringLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\!\\%\\^\\&\\*\\-\\+\\=\\~\\|\\<\\>\\?\\/]", PaletteIndex::Operator)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\(\\)\\;\\,\\.]", PaletteIndex::Separator)); - langDef.m_commentStart = "/*"; - langDef.m_commentEnd = "*/"; - langDef.m_singleLineComment = "//"; - - langDef.m_caseSensitive = true; - langDef.m_autoIndentation = true; - - langDef.m_name = "HLSL"; - - inited = true; + return langDef; } - return langDef; -} -const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::GLSL() { - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) { - static const char *const keywords[] = { - "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local" - }; - for (auto &k : keywords) - langDef.m_keywords.insert(k); + const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::C() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const keywords[] = { + "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", + "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", + "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", + "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", + "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local" + }; + for (auto &k: keywords) + langDef.m_keywords.insert(k); - static const char *const identifiers[] = { - "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper" - }; - for (auto &k : identifiers) { - Identifier id; - id.m_declaration = "Built-in function"; - langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); + static const char *const identifiers[] = { + "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", + "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", + "isdigit", "isgraph", "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", + "modf", "pow", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", + "strcat", "strcmp", "strerror", "time", "tolower", "toupper" + }; + for (auto &k: identifiers) { + Identifier id; + id.m_declaration = "Built-in function"; + langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.m_tokenize = [](strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end, PaletteIndex &paletteIndex) -> bool { + paletteIndex = PaletteIndex::Max; + + while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin)) + in_begin++; + + if (in_begin == in_end) { + out_begin = in_end; + out_end = in_end; + paletteIndex = PaletteIndex::Default; + } else if (tokenizeCStyleString(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::StringLiteral; + else if (tokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::CharLiteral; + else if (tokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Identifier; + else if (tokenizeCStyleNumber(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::NumericLiteral; + else if (tokenizeCStyleOperator(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Operator; + else if (tokenizeCStyleSeparator(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Separator; + + return paletteIndex != PaletteIndex::Max; + }; + + langDef.m_commentStart = "/*"; + langDef.m_commentEnd = "*/"; + langDef.m_singleLineComment = "//"; + + langDef.m_caseSensitive = true; + langDef.m_autoIndentation = true; + + langDef.m_name = "C"; + + inited = true; } - - langDef.m_tokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Directive)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::StringLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\!\\%\\^\\&\\*\\-\\+\\=\\~\\|\\<\\>\\?\\/]", PaletteIndex::Operator)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\(\\)\\;\\,\\.]", PaletteIndex::Separator)); - langDef.m_commentStart = "/*"; - langDef.m_commentEnd = "*/"; - langDef.m_singleLineComment = "//"; - - langDef.m_caseSensitive = true; - langDef.m_autoIndentation = true; - - langDef.m_name = "GLSL"; - - inited = true; + return langDef; } - return langDef; -} -const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::C() { - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) { - static const char *const keywords[] = { - "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local" - }; - for (auto &k : keywords) - langDef.m_keywords.insert(k); + const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::SQL() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const keywords[] = { + "ADD", "EXCEPT", "PERCENT", "ALL", "EXEC", "PLAN", "ALTER", "EXECUTE", "PRECISION", "AND", "EXISTS", + "PRIMARY", "ANY", "EXIT", "PRINT", "AS", "FETCH", "PROC", "ASC", "FILE", "PROCEDURE", + "AUTHORIZATION", "FILLFACTOR", "PUBLIC", "BACKUP", "FOR", "RAISERROR", "BEGIN", "FOREIGN", "READ", + "BETWEEN", "FREETEXT", "READTEXT", "BREAK", "FREETEXTTABLE", "RECONFIGURE", "BROWSE", "FROM", + "REFERENCES", "BULK", "FULL", "REPLICATION", "BY", "FUNCTION", "RESTORE", "CASCADE", "GOTO", + "RESTRICT", "CASE", "GRANT", "RETURN", "CHECK", "GROUP", "REVOKE", "CHECKPOINT", "HAVING", "RIGHT", + "CLOSE", "HOLDLOCK", "ROLLBACK", "CLUSTERED", "IDENTITY", "ROWCOUNT", "COALESCE", "IDENTITY_INSERT", + "ROWGUIDCOL", "COLLATE", "IDENTITYCOL", "RULE", "COLUMN", "IF", "SAVE", "COMMIT", "IN", "SCHEMA", + "COMPUTE", "INDEX", "SELECT", "CONSTRAINT", "INNER", "SESSION_USER", "CONTAINS", "INSERT", "SET", + "CONTAINSTABLE", "INTERSECT", "SETUSER", "CONTINUE", "INTO", "SHUTDOWN", "CONVERT", "IS", "SOME", + "CREATE", "JOIN", "STATISTICS", "CROSS", "KEY", "SYSTEM_USER", "CURRENT", "KILL", "TABLE", + "CURRENT_DATE", "LEFT", "TEXTSIZE", "CURRENT_TIME", "LIKE", "THEN", "CURRENT_TIMESTAMP", "LINENO", + "TO", "CURRENT_USER", "LOAD", "TOP", "CURSOR", "NATIONAL", "TRAN", "DATABASE", "NOCHECK", + "TRANSACTION", "DBCC", "NONCLUSTERED", "TRIGGER", "DEALLOCATE", "NOT", "TRUNCATE", "DECLARE", + "NULL", "TSEQUAL", "DEFAULT", "NULLIF", "UNION", "DELETE", "OF", "UNIQUE", "DENY", "OFF", "UPDATE", + "DESC", "OFFSETS", "UPDATETEXT", "DISK", "ON", "USE", "DISTINCT", "OPEN", "USER", "DISTRIBUTED", + "OPENDATASOURCE", "VALUES", "DOUBLE", "OPENQUERY", "VARYING", "DROP", "OPENROWSET", "VIEW", "DUMMY", + "OPENXML", "WAITFOR", "DUMP", "OPTION", "WHEN", "ELSE", "OR", "WHERE", "END", "ORDER", "WHILE", + "ERRLVL", "OUTER", "WITH", "ESCAPE", "OVER", "WRITETEXT" + }; - static const char *const identifiers[] = { - "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper" - }; - for (auto &k : identifiers) { - Identifier id; - id.m_declaration = "Built-in function"; - langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); + for (auto &k: keywords) + langDef.m_keywords.insert(k); + + static const char *const identifiers[] = { + "ABS", "ACOS", "ADD_MONTHS", "ASCII", "ASCIISTR", "ASIN", "ATAN", "ATAN2", "AVG", "BFILENAME", + "BIN_TO_NUM", "BITAND", "CARDINALITY", "CASE", "CAST", "CEIL", "CHARTOROWID", "CHR", "COALESCE", + "COMPOSE", "CONCAT", "CONVERT", "CORR", "COS", "COSH", "COUNT", "COVAR_POP", "COVAR_SAMP", + "CUME_DIST", "CURRENT_DATE", "CURRENT_TIMESTAMP", "DBTIMEZONE", "DECODE", "DECOMPOSE", "DENSE_RANK", + "DUMP", "EMPTY_BLOB", "EMPTY_CLOB", "EXP", "EXTRACT", "FIRST_VALUE", "FLOOR", "FROM_TZ", "GREATEST", + "GROUP_ID", "HEXTORAW", "INITCAP", "INSTR", "INSTR2", "INSTR4", "INSTRB", "INSTRC", "LAG", + "LAST_DAY", "LAST_VALUE", "LEAD", "LEAST", "LENGTH", "LENGTH2", "LENGTH4", "LENGTHB", "LENGTHC", + "LISTAGG", "LN", "LNNVL", "LOCALTIMESTAMP", "LOG", "LOWER", "LPAD", "LTRIM", "MAX", "MEDIAN", "MIN", + "MOD", "MONTHS_BETWEEN", "NANVL", "NCHR", "NEW_TIME", "NEXT_DAY", "NTH_VALUE", "NULLIF", + "NUMTODSINTERVAL", "NUMTOYMINTERVAL", "NVL", "NVL2", "POWER", "RANK", "RAWTOHEX", "REGEXP_COUNT", + "REGEXP_INSTR", "REGEXP_REPLACE", "REGEXP_SUBSTR", "REMAINDER", "REPLACE", "ROUND", "ROWNUM", + "RPAD", "RTRIM", "SESSIONTIMEZONE", "SIGN", "SIN", "SINH", "SOUNDEX", "SQRT", "STDDEV", "SUBSTR", + "SUM", "SYS_CONTEXT", "SYSDATE", "SYSTIMESTAMP", "TAN", "TANH", "TO_CHAR", "TO_CLOB", "TO_DATE", + "TO_DSINTERVAL", "TO_LOB", "TO_MULTI_BYTE", "TO_NCLOB", "TO_NUMBER", "TO_SINGLE_BYTE", + "TO_TIMESTAMP", "TO_TIMESTAMP_TZ", "TO_YMINTERVAL", "TRANSLATE", "TRIM", "TRUNC", "TZ_OFFSET", + "UID", "UPPER", "USER", "USERENV", "VAR_POP", "VAR_SAMP", "VARIANCE", "VSIZE " + }; + for (auto &k: identifiers) { + Identifier id; + id.m_declaration = "Built-in function"; + langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.m_tokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Directive)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::StringLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\!\\%\\^\\&\\*\\-\\+\\=\\~\\|\\<\\>\\?\\/]", PaletteIndex::Operator)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\(\\)\\;\\,\\.]", PaletteIndex::Separator)); + + langDef.m_commentStart = "/*"; + langDef.m_commentEnd = "*/"; + langDef.m_singleLineComment = "//"; + + langDef.m_caseSensitive = false; + langDef.m_autoIndentation = false; + + langDef.m_name = "SQL"; + + inited = true; } - - langDef.m_tokenize = [](strConstIter in_begin, strConstIter in_end, strConstIter &out_begin, strConstIter &out_end, PaletteIndex &paletteIndex) -> bool { - paletteIndex = PaletteIndex::Max; - - while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin)) - in_begin++; - - if (in_begin == in_end) { - out_begin = in_end; - out_end = in_end; - paletteIndex = PaletteIndex::Default; - } else if (tokenizeCStyleString(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::StringLiteral; - else if (tokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::CharLiteral; - else if (tokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::Identifier; - else if (tokenizeCStyleNumber(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::NumericLiteral; - else if (tokenizeCStyleOperator(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::Operator; - else if (tokenizeCStyleSeparator(in_begin, in_end, out_begin, out_end)) - paletteIndex = PaletteIndex::Separator; - - return paletteIndex != PaletteIndex::Max; - }; - - langDef.m_commentStart = "/*"; - langDef.m_commentEnd = "*/"; - langDef.m_singleLineComment = "//"; - - langDef.m_caseSensitive = true; - langDef.m_autoIndentation = true; - - langDef.m_name = "C"; - - inited = true; + return langDef; } - return langDef; -} -const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::SQL() { - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) { - static const char *const keywords[] = { - "ADD", "EXCEPT", "PERCENT", "ALL", "EXEC", "PLAN", "ALTER", "EXECUTE", "PRECISION", "AND", "EXISTS", "PRIMARY", "ANY", "EXIT", "PRINT", "AS", "FETCH", "PROC", "ASC", "FILE", "PROCEDURE", "AUTHORIZATION", "FILLFACTOR", "PUBLIC", "BACKUP", "FOR", "RAISERROR", "BEGIN", "FOREIGN", "READ", "BETWEEN", "FREETEXT", "READTEXT", "BREAK", "FREETEXTTABLE", "RECONFIGURE", "BROWSE", "FROM", "REFERENCES", "BULK", "FULL", "REPLICATION", "BY", "FUNCTION", "RESTORE", "CASCADE", "GOTO", "RESTRICT", "CASE", "GRANT", "RETURN", "CHECK", "GROUP", "REVOKE", "CHECKPOINT", "HAVING", "RIGHT", "CLOSE", "HOLDLOCK", "ROLLBACK", "CLUSTERED", "IDENTITY", "ROWCOUNT", "COALESCE", "IDENTITY_INSERT", "ROWGUIDCOL", "COLLATE", "IDENTITYCOL", "RULE", "COLUMN", "IF", "SAVE", "COMMIT", "IN", "SCHEMA", "COMPUTE", "INDEX", "SELECT", "CONSTRAINT", "INNER", "SESSION_USER", "CONTAINS", "INSERT", "SET", "CONTAINSTABLE", "INTERSECT", "SETUSER", "CONTINUE", "INTO", "SHUTDOWN", "CONVERT", "IS", "SOME", "CREATE", "JOIN", "STATISTICS", "CROSS", "KEY", "SYSTEM_USER", "CURRENT", "KILL", "TABLE", "CURRENT_DATE", "LEFT", "TEXTSIZE", "CURRENT_TIME", "LIKE", "THEN", "CURRENT_TIMESTAMP", "LINENO", "TO", "CURRENT_USER", "LOAD", "TOP", "CURSOR", "NATIONAL", "TRAN", "DATABASE", "NOCHECK", "TRANSACTION", "DBCC", "NONCLUSTERED", "TRIGGER", "DEALLOCATE", "NOT", "TRUNCATE", "DECLARE", "NULL", "TSEQUAL", "DEFAULT", "NULLIF", "UNION", "DELETE", "OF", "UNIQUE", "DENY", "OFF", "UPDATE", "DESC", "OFFSETS", "UPDATETEXT", "DISK", "ON", "USE", "DISTINCT", "OPEN", "USER", "DISTRIBUTED", "OPENDATASOURCE", "VALUES", "DOUBLE", "OPENQUERY", "VARYING", "DROP", "OPENROWSET", "VIEW", "DUMMY", "OPENXML", "WAITFOR", "DUMP", "OPTION", "WHEN", "ELSE", "OR", "WHERE", "END", "ORDER", "WHILE", "ERRLVL", "OUTER", "WITH", "ESCAPE", "OVER", "WRITETEXT" - }; + const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::AngelScript() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const keywords[] = { + "and", "abstract", "auto", "bool", "break", "case", "cast", "class", "const", "continue", "default", + "do", "double", "else", "enum", "false", "final", "float", "for", "from", "funcdef", "function", + "get", "if", "import", "in", "inout", "int", "interface", "int8", "int16", "int32", "int64", "is", + "mixin", "namespace", "not", "null", "or", "out", "override", "private", "protected", "return", + "set", "shared", "super", "switch", "this ", "true", "typedef", "uint", "uint8", "uint16", "uint32", + "uint64", "void", "while", "xor" + }; - for (auto &k : keywords) - langDef.m_keywords.insert(k); + for (auto &k: keywords) + langDef.m_keywords.insert(k); - static const char *const identifiers[] = { - "ABS", "ACOS", "ADD_MONTHS", "ASCII", "ASCIISTR", "ASIN", "ATAN", "ATAN2", "AVG", "BFILENAME", "BIN_TO_NUM", "BITAND", "CARDINALITY", "CASE", "CAST", "CEIL", "CHARTOROWID", "CHR", "COALESCE", "COMPOSE", "CONCAT", "CONVERT", "CORR", "COS", "COSH", "COUNT", "COVAR_POP", "COVAR_SAMP", "CUME_DIST", "CURRENT_DATE", "CURRENT_TIMESTAMP", "DBTIMEZONE", "DECODE", "DECOMPOSE", "DENSE_RANK", "DUMP", "EMPTY_BLOB", "EMPTY_CLOB", "EXP", "EXTRACT", "FIRST_VALUE", "FLOOR", "FROM_TZ", "GREATEST", "GROUP_ID", "HEXTORAW", "INITCAP", "INSTR", "INSTR2", "INSTR4", "INSTRB", "INSTRC", "LAG", "LAST_DAY", "LAST_VALUE", "LEAD", "LEAST", "LENGTH", "LENGTH2", "LENGTH4", "LENGTHB", "LENGTHC", "LISTAGG", "LN", "LNNVL", "LOCALTIMESTAMP", "LOG", "LOWER", "LPAD", "LTRIM", "MAX", "MEDIAN", "MIN", "MOD", "MONTHS_BETWEEN", "NANVL", "NCHR", "NEW_TIME", "NEXT_DAY", "NTH_VALUE", "NULLIF", "NUMTODSINTERVAL", "NUMTOYMINTERVAL", "NVL", "NVL2", "POWER", "RANK", "RAWTOHEX", "REGEXP_COUNT", "REGEXP_INSTR", "REGEXP_REPLACE", "REGEXP_SUBSTR", "REMAINDER", "REPLACE", "ROUND", "ROWNUM", "RPAD", "RTRIM", "SESSIONTIMEZONE", "SIGN", "SIN", "SINH", "SOUNDEX", "SQRT", "STDDEV", "SUBSTR", "SUM", "SYS_CONTEXT", "SYSDATE", "SYSTIMESTAMP", "TAN", "TANH", "TO_CHAR", "TO_CLOB", "TO_DATE", "TO_DSINTERVAL", "TO_LOB", "TO_MULTI_BYTE", "TO_NCLOB", "TO_NUMBER", "TO_SINGLE_BYTE", "TO_TIMESTAMP", "TO_TIMESTAMP_TZ", "TO_YMINTERVAL", "TRANSLATE", "TRIM", "TRUNC", "TZ_OFFSET", "UID", "UPPER", "USER", "USERENV", "VAR_POP", "VAR_SAMP", "VARIANCE", "VSIZE " - }; - for (auto &k : identifiers) { - Identifier id; - id.m_declaration = "Built-in function"; - langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); + static const char *const identifiers[] = { + "cos", "sin", "tab", "acos", "asin", "atan", "atan2", "cosh", "sinh", "tanh", "log", "log10", "pow", + "sqrt", "abs", "ceil", "floor", "fraction", "closeTo", "fpFromIEEE", "fpToIEEE", "complex", + "opEquals", "opAddAssign", "opSubAssign", "opMulAssign", "opDivAssign", "opAdd", "opSub", "opMul", + "opDiv" + }; + for (auto &k: identifiers) { + Identifier id; + id.m_declaration = "Built-in function"; + langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.m_tokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Directive)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::StringLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\!\\%\\^\\&\\*\\-\\+\\=\\~\\|\\<\\>\\?\\/]", PaletteIndex::Operator)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\(\\)\\;\\,\\.]", PaletteIndex::Separator)); + + langDef.m_commentStart = "/*"; + langDef.m_commentEnd = "*/"; + langDef.m_singleLineComment = "//"; + + langDef.m_caseSensitive = true; + langDef.m_autoIndentation = true; + + langDef.m_name = "AngelScript"; + + inited = true; } - - langDef.m_tokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Directive)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::StringLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\!\\%\\^\\&\\*\\-\\+\\=\\~\\|\\<\\>\\?\\/]", PaletteIndex::Operator)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\(\\)\\;\\,\\.]", PaletteIndex::Separator)); - - langDef.m_commentStart = "/*"; - langDef.m_commentEnd = "*/"; - langDef.m_singleLineComment = "//"; - - langDef.m_caseSensitive = false; - langDef.m_autoIndentation = false; - - langDef.m_name = "SQL"; - - inited = true; + return langDef; } - return langDef; -} -const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::AngelScript() { - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) { - static const char *const keywords[] = { - "and", "abstract", "auto", "bool", "break", "case", "cast", "class", "const", "continue", "default", "do", "double", "else", "enum", "false", "final", "float", "for", "from", "funcdef", "function", "get", "if", "import", "in", "inout", "int", "interface", "int8", "int16", "int32", "int64", "is", "mixin", "namespace", "not", "null", "or", "out", "override", "private", "protected", "return", "set", "shared", "super", "switch", "this ", "true", "typedef", "uint", "uint8", "uint16", "uint32", "uint64", "void", "while", "xor" - }; + const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::Lua() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char *const keywords[] = { + "and", "break", "do", "", "else", "elseif", "end", "false", "for", "function", "if", "in", "", + "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while" + }; - for (auto &k : keywords) - langDef.m_keywords.insert(k); + for (auto &k: keywords) + langDef.m_keywords.insert(k); - static const char *const identifiers[] = { - "cos", "sin", "tab", "acos", "asin", "atan", "atan2", "cosh", "sinh", "tanh", "log", "log10", "pow", "sqrt", "abs", "ceil", "floor", "fraction", "closeTo", "fpFromIEEE", "fpToIEEE", "complex", "opEquals", "opAddAssign", "opSubAssign", "opMulAssign", "opDivAssign", "opAdd", "opSub", "opMul", "opDiv" - }; - for (auto &k : identifiers) { - Identifier id; - id.m_declaration = "Built-in function"; - langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); + static const char *const identifiers[] = { + "assert", "collectgarbage", "dofile", "error", "getmetatable", "ipairs", "loadfile", "load", + "loadstring", "next", "pairs", "pcall", "print", "rawequal", "rawlen", "rawget", "rawset", "select", + "setmetatable", "tonumber", "tostring", "type", "xpcall", "_G", "_VERSION", "arshift", "band", + "bnot", "bor", "bxor", "btest", "extract", "lrotate", "lshift", "replace", "rrotate", "rshift", + "create", "resume", "running", "status", "wrap", "yield", "isyieldable", "debug", "getuservalue", + "gethook", "getinfo", "getlocal", "getregistry", "getmetatable", "getupvalue", "upvaluejoin", + "upvalueid", "setuservalue", "sethook", "setlocal", "setmetatable", "setupvalue", "traceback", + "close", "flush", "input", "lines", "open", "output", "popen", "read", "tmpfile", "type", "write", + "close", "flush", "lines", "read", "seek", "setvbuf", "write", "__gc", "__tostring", "abs", "acos", + "asin", "atan", "ceil", "cos", "deg", "exp", "tointeger", "floor", "fmod", "ult", "log", "max", + "min", "modf", "rad", "random", "randomseed", "sin", "sqrt", "string", "tan", "type", "atan2", + "cosh", "sinh", "tanh", "pow", "frexp", "ldexp", "log10", "pi", "huge", "maxinteger", "mininteger", + "loadlib", "searchpath", "seeall", "preload", "cpath", "path", "searchers", "loaded", "module", + "require", "clock", "date", "difftime", "execute", "exit", "getenv", "remove", "rename", + "setlocale", "time", "tmpname", "byte", "char", "dump", "find", "format", "gmatch", "gsub", "len", + "lower", "match", "rep", "reverse", "sub", "upper", "pack", "packsize", "unpack", "concat", "maxn", + "insert", "pack", "unpack", "remove", "move", "sort", "offset", "codepoint", "char", "len", "codes", + "charpattern", "coroutine", "table", "io", "os", "string", "utf8", "bit32", "math", "debug", + "package" + }; + for (auto &k: identifiers) { + Identifier id; + id.m_declaration = "Built-in function"; + langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.m_tokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Directive)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::StringLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\!\\%\\^\\&\\*\\-\\+\\=\\~\\|\\<\\>\\?\\/]", PaletteIndex::Operator)); + langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\(\\)\\;\\,\\.]", PaletteIndex::Separator)); + + langDef.m_commentStart = "--[["; + langDef.m_commentEnd = "]]"; + langDef.m_singleLineComment = "--"; + + langDef.m_caseSensitive = true; + langDef.m_autoIndentation = false; + + langDef.m_name = "Lua"; + + inited = true; } - - langDef.m_tokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Directive)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::StringLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\!\\%\\^\\&\\*\\-\\+\\=\\~\\|\\<\\>\\?\\/]", PaletteIndex::Operator)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\(\\)\\;\\,\\.]", PaletteIndex::Separator)); - - langDef.m_commentStart = "/*"; - langDef.m_commentEnd = "*/"; - langDef.m_singleLineComment = "//"; - - langDef.m_caseSensitive = true; - langDef.m_autoIndentation = true; - - langDef.m_name = "AngelScript"; - - inited = true; + return langDef; } - return langDef; -} - -const TextEditor::LanguageDefinition &TextEditor::LanguageDefinition::Lua() { - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) { - static const char *const keywords[] = { - "and", "break", "do", "", "else", "elseif", "end", "false", "for", "function", "if", "in", "", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while" - }; - - for (auto &k : keywords) - langDef.m_keywords.insert(k); - - static const char *const identifiers[] = { - "assert", "collectgarbage", "dofile", "error", "getmetatable", "ipairs", "loadfile", "load", "loadstring", "next", "pairs", "pcall", "print", "rawequal", "rawlen", "rawget", "rawset", "select", "setmetatable", "tonumber", "tostring", "type", "xpcall", "_G", "_VERSION", "arshift", "band", "bnot", "bor", "bxor", "btest", "extract", "lrotate", "lshift", "replace", "rrotate", "rshift", "create", "resume", "running", "status", "wrap", "yield", "isyieldable", "debug", "getuservalue", "gethook", "getinfo", "getlocal", "getregistry", "getmetatable", "getupvalue", "upvaluejoin", "upvalueid", "setuservalue", "sethook", "setlocal", "setmetatable", "setupvalue", "traceback", "close", "flush", "input", "lines", "open", "output", "popen", "read", "tmpfile", "type", "write", "close", "flush", "lines", "read", "seek", "setvbuf", "write", "__gc", "__tostring", "abs", "acos", "asin", "atan", "ceil", "cos", "deg", "exp", "tointeger", "floor", "fmod", "ult", "log", "max", "min", "modf", "rad", "random", "randomseed", "sin", "sqrt", "string", "tan", "type", "atan2", "cosh", "sinh", "tanh", "pow", "frexp", "ldexp", "log10", "pi", "huge", "maxinteger", "mininteger", "loadlib", "searchpath", "seeall", "preload", "cpath", "path", "searchers", "loaded", "module", "require", "clock", "date", "difftime", "execute", "exit", "getenv", "remove", "rename", "setlocale", "time", "tmpname", "byte", "char", "dump", "find", "format", "gmatch", "gsub", "len", "lower", "match", "rep", "reverse", "sub", "upper", "pack", "packsize", "unpack", "concat", "maxn", "insert", "pack", "unpack", "remove", "move", "sort", "offset", "codepoint", "char", "len", "codes", "charpattern", "coroutine", "table", "io", "os", "string", "utf8", "bit32", "math", "debug", "package" - }; - for (auto &k : identifiers) { - Identifier id; - id.m_declaration = "Built-in function"; - langDef.m_identifiers.insert(std::make_pair(std::string(k), id)); - } - - langDef.m_tokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Directive)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::StringLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::NumericLiteral)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\!\\%\\^\\&\\*\\-\\+\\=\\~\\|\\<\\>\\?\\/]", PaletteIndex::Operator)); - langDef.m_tokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\(\\)\\;\\,\\.]", PaletteIndex::Separator)); - - langDef.m_commentStart = "--[["; - langDef.m_commentEnd = "]]"; - langDef.m_singleLineComment = "--"; - - langDef.m_caseSensitive = true; - langDef.m_autoIndentation = false; - - langDef.m_name = "Lua"; - - inited = true; - } - return langDef; -} +} \ No newline at end of file diff --git a/plugins/builtin/include/content/text_highlighting/pattern_language.hpp b/plugins/builtin/include/content/text_highlighting/pattern_language.hpp index 5961e2525..ca07fc69b 100644 --- a/plugins/builtin/include/content/text_highlighting/pattern_language.hpp +++ b/plugins/builtin/include/content/text_highlighting/pattern_language.hpp @@ -27,9 +27,9 @@ namespace hex::plugin::builtin { using TokenIter = pl::hlp::SafeIterator::const_iterator>; using VariableScopes = std::map; using Inheritances = std::map>; - using IdentifierTypeColor = std::map; - using TokenTypeColor = std::map; - using TokenColor = std::map; + using IdentifierTypeColor = std::map; + using TokenTypeColor = std::map; + using TokenColor = std::map; struct ParentDefinition; struct Definition { @@ -218,7 +218,7 @@ namespace hex::plugin::builtin { /// Loads the source code and calculates the first token index of each line void loadText(); /// Used to obtain the color to be applied. - TextEditor::PaletteIndex getPaletteIndex(Token::Literal *literal); + ui::TextEditor::PaletteIndex getPaletteIndex(Token::Literal *literal); /// The complement of a set is also known as its inverse void invertGlobalTokenRange(); /// Starting at the identifier, it tracks all the scope resolution and dot operators and returns the full chain without arrays, templates, pointers,... @@ -269,8 +269,8 @@ namespace hex::plugin::builtin { ///Creates a map from function name to argument type void linkAttribute(); /// Comment and strings usethese function to determine their coordinates - template TextEditor::Coordinates commentCoordinates(Token *token); - TextEditor::Coordinates stringCoordinates(); + template ui::TextEditor::Coordinates commentCoordinates(Token *token); + ui::TextEditor::Coordinates stringCoordinates(); /// Returns the number of tasks highlighting code. Shouldn't be > 1 i32 getRunningColorizers() { return m_runningColorizers; diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index 8172b1500..0c0a00221 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -73,7 +73,7 @@ namespace hex::plugin::builtin { return &m_editorRuntime; } - TextEditor &getTextEditor() { + ui::TextEditor &getTextEditor() { return m_textEditor; } @@ -262,7 +262,7 @@ namespace hex::plugin::builtin { PerProvider m_hasUnevaluatedChanges; std::chrono::time_point m_lastEditorChangeTime; - PerProvider m_textEditor, m_consoleEditor; + PerProvider m_textEditor, m_consoleEditor; std::atomic m_consoleNeedsUpdate = false; std::atomic m_dangerousFunctionCalled = false; @@ -279,15 +279,15 @@ namespace hex::plugin::builtin { std::mutex m_logMutex; - PerProvider m_cursorPosition; + PerProvider m_cursorPosition; - PerProvider m_consoleCursorPosition; + 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_breakpoints; PerProvider> m_lastEvaluationError; PerProvider> m_lastCompileError; PerProvider*> m_callStack; @@ -343,8 +343,8 @@ namespace hex::plugin::builtin { void drawPatternTooltip(pl::ptrn::Pattern *pattern); - void drawTextEditorFindReplacePopup(TextEditor *textEditor); - void drawTextEditorGotoLinePopup(TextEditor *textEditor); + void drawTextEditorFindReplacePopup(ui::TextEditor *textEditor); + void drawTextEditorGotoLinePopup(ui::TextEditor *textEditor); void historyInsert(std::array &history, u32 &size, u32 &index, const std::string &value); @@ -355,9 +355,9 @@ namespace hex::plugin::builtin { void parsePattern(const std::string &code, prv::Provider *provider); void evaluatePattern(const std::string &code, prv::Provider *provider); - TextEditor *getEditorFromFocusedWindow(); - void setupFindReplace(TextEditor *editor); - void setupGotoLine(TextEditor *editor); + ui::TextEditor *getEditorFromFocusedWindow(); + void setupFindReplace(ui::TextEditor *editor); + void setupGotoLine(ui::TextEditor *editor); void registerEvents(); void registerMenuItems(); diff --git a/plugins/builtin/source/content/text_highlighting/pattern_language.cpp b/plugins/builtin/source/content/text_highlighting/pattern_language.cpp index 1b5f715df..1e019a24d 100644 --- a/plugins/builtin/source/content/text_highlighting/pattern_language.cpp +++ b/plugins/builtin/source/content/text_highlighting/pattern_language.cpp @@ -1176,52 +1176,52 @@ namespace hex::plugin::builtin { } const TextHighlighter::TokenTypeColor TextHighlighter::m_tokenTypeColor = { - {Token::Type::Keyword, TextEditor::PaletteIndex::Keyword}, - {Token::Type::ValueType, TextEditor::PaletteIndex::BuiltInType}, - {Token::Type::Operator, TextEditor::PaletteIndex::Operator}, - {Token::Type::Separator, TextEditor::PaletteIndex::Separator}, - {Token::Type::String, TextEditor::PaletteIndex::StringLiteral}, - {Token::Type::Directive, TextEditor::PaletteIndex::Directive}, - {Token::Type::Comment, TextEditor::PaletteIndex::Comment}, - {Token::Type::Integer, TextEditor::PaletteIndex::NumericLiteral}, - {Token::Type::Identifier, TextEditor::PaletteIndex::Identifier}, - {Token::Type::DocComment, TextEditor::PaletteIndex::DocComment} + {Token::Type::Keyword, ui::TextEditor::PaletteIndex::Keyword}, + {Token::Type::ValueType, ui::TextEditor::PaletteIndex::BuiltInType}, + {Token::Type::Operator, ui::TextEditor::PaletteIndex::Operator}, + {Token::Type::Separator, ui::TextEditor::PaletteIndex::Separator}, + {Token::Type::String, ui::TextEditor::PaletteIndex::StringLiteral}, + {Token::Type::Directive, ui::TextEditor::PaletteIndex::Directive}, + {Token::Type::Comment, ui::TextEditor::PaletteIndex::Comment}, + {Token::Type::Integer, ui::TextEditor::PaletteIndex::NumericLiteral}, + {Token::Type::Identifier, ui::TextEditor::PaletteIndex::Identifier}, + {Token::Type::DocComment, ui::TextEditor::PaletteIndex::DocComment} }; const TextHighlighter::IdentifierTypeColor TextHighlighter::m_identifierTypeColor = { - {Identifier::IdentifierType::Macro, TextEditor::PaletteIndex::PreprocIdentifier}, - {Identifier::IdentifierType::UDT, TextEditor::PaletteIndex::UserDefinedType}, - {Identifier::IdentifierType::Function, TextEditor::PaletteIndex::Function}, - {Identifier::IdentifierType::Attribute, TextEditor::PaletteIndex::Attribute}, - {Identifier::IdentifierType::NameSpace, TextEditor::PaletteIndex::NameSpace}, - {Identifier::IdentifierType::Typedef, TextEditor::PaletteIndex::TypeDef}, - {Identifier::IdentifierType::PatternVariable, TextEditor::PaletteIndex::PatternVariable}, - {Identifier::IdentifierType::LocalVariable, TextEditor::PaletteIndex::LocalVariable}, - {Identifier::IdentifierType::CalculatedPointer, TextEditor::PaletteIndex::CalculatedPointer}, - {Identifier::IdentifierType::TemplateArgument, TextEditor::PaletteIndex::TemplateArgument}, - {Identifier::IdentifierType::PlacedVariable, TextEditor::PaletteIndex::PlacedVariable}, - {Identifier::IdentifierType::View, TextEditor::PaletteIndex::View}, - {Identifier::IdentifierType::FunctionVariable, TextEditor::PaletteIndex::FunctionVariable}, - {Identifier::IdentifierType::FunctionParameter, TextEditor::PaletteIndex::FunctionParameter}, - {Identifier::IdentifierType::Unknown, TextEditor::PaletteIndex::UnkIdentifier}, - {Identifier::IdentifierType::FunctionUnknown, TextEditor::PaletteIndex::UnkIdentifier}, - {Identifier::IdentifierType::MemberUnknown, TextEditor::PaletteIndex::UnkIdentifier}, - {Identifier::IdentifierType::ScopeResolutionUnknown, TextEditor::PaletteIndex::UnkIdentifier}, - {Identifier::IdentifierType::GlobalVariable, TextEditor::PaletteIndex::GlobalVariable}, + {Identifier::IdentifierType::Macro, ui::TextEditor::PaletteIndex::PreprocIdentifier}, + {Identifier::IdentifierType::UDT, ui::TextEditor::PaletteIndex::UserDefinedType}, + {Identifier::IdentifierType::Function, ui::TextEditor::PaletteIndex::Function}, + {Identifier::IdentifierType::Attribute, ui::TextEditor::PaletteIndex::Attribute}, + {Identifier::IdentifierType::NameSpace, ui::TextEditor::PaletteIndex::NameSpace}, + {Identifier::IdentifierType::Typedef, ui::TextEditor::PaletteIndex::TypeDef}, + {Identifier::IdentifierType::PatternVariable, ui::TextEditor::PaletteIndex::PatternVariable}, + {Identifier::IdentifierType::LocalVariable, ui::TextEditor::PaletteIndex::LocalVariable}, + {Identifier::IdentifierType::CalculatedPointer, ui::TextEditor::PaletteIndex::CalculatedPointer}, + {Identifier::IdentifierType::TemplateArgument, ui::TextEditor::PaletteIndex::TemplateArgument}, + {Identifier::IdentifierType::PlacedVariable, ui::TextEditor::PaletteIndex::PlacedVariable}, + {Identifier::IdentifierType::View, ui::TextEditor::PaletteIndex::View}, + {Identifier::IdentifierType::FunctionVariable, ui::TextEditor::PaletteIndex::FunctionVariable}, + {Identifier::IdentifierType::FunctionParameter, ui::TextEditor::PaletteIndex::FunctionParameter}, + {Identifier::IdentifierType::Unknown, ui::TextEditor::PaletteIndex::UnkIdentifier}, + {Identifier::IdentifierType::FunctionUnknown, ui::TextEditor::PaletteIndex::UnkIdentifier}, + {Identifier::IdentifierType::MemberUnknown, ui::TextEditor::PaletteIndex::UnkIdentifier}, + {Identifier::IdentifierType::ScopeResolutionUnknown, ui::TextEditor::PaletteIndex::UnkIdentifier}, + {Identifier::IdentifierType::GlobalVariable, ui::TextEditor::PaletteIndex::GlobalVariable}, }; // Second paletteIndex called from processLineTokens to process literals - TextEditor::PaletteIndex TextHighlighter::getPaletteIndex(Literal *literal) { + ui::TextEditor::PaletteIndex TextHighlighter::getPaletteIndex(Literal *literal) { if (literal->isFloatingPoint() || literal->isSigned() || literal->isUnsigned()) - return TextEditor::PaletteIndex::NumericLiteral; + return ui::TextEditor::PaletteIndex::NumericLiteral; - else if (literal->isCharacter() || literal->isBoolean()) return TextEditor::PaletteIndex::CharLiteral; + else if (literal->isCharacter() || literal->isBoolean()) return ui::TextEditor::PaletteIndex::CharLiteral; - else if (literal->isString()) return TextEditor::PaletteIndex::StringLiteral; + else if (literal->isString()) return ui::TextEditor::PaletteIndex::StringLiteral; - else return TextEditor::PaletteIndex::Default; + else return ui::TextEditor::PaletteIndex::Default; } // Render the compilation errors using squiggly lines @@ -1239,13 +1239,13 @@ namespace hex::plugin::builtin { return wolv::util::combineStrings(lines, "\n"); }; - TextEditor::ErrorMarkers errorMarkers; + ui::TextEditor::ErrorMarkers errorMarkers; if (!m_compileErrors.empty()) { for (const auto &error: m_compileErrors) { if (isLocationValid(error.getLocation())) { - auto key = TextEditor::Coordinates(error.getLocation().line, error.getLocation().column); + auto key = ui::TextEditor::Coordinates(error.getLocation().line, error.getLocation().column); if (!errorMarkers.contains(key) || errorMarkers[key].first < error.getLocation().length) errorMarkers[key] = std::make_pair(error.getLocation().length, processMessage(error.getMessage())); @@ -1399,7 +1399,7 @@ namespace hex::plugin::builtin { else token = const_cast(&m_tokens[tokenId]); - if (token->type == Token::Type::Identifier && (!m_tokenColors.contains(token) || m_tokenColors.at(token) == TextEditor::PaletteIndex::Default || m_tokenColors.at(token) == TextEditor::PaletteIndex::UnkIdentifier)) + if (token->type == Token::Type::Identifier && (!m_tokenColors.contains(token) || m_tokenColors.at(token) == ui::TextEditor::PaletteIndex::Default || m_tokenColors.at(token) == ui::TextEditor::PaletteIndex::UnkIdentifier)) m_tokenColors[token] = m_identifierTypeColor.at(type); else if (!m_tokenColors.contains(token)) m_tokenColors[token] = m_tokenTypeColor.at(token->type); @@ -1426,11 +1426,11 @@ namespace hex::plugin::builtin { if (docComment != nullptr && !m_tokenColors.contains(token)) { if (docComment->singleLine) - m_tokenColors[token] = TextEditor::PaletteIndex::DocComment; + m_tokenColors[token] = ui::TextEditor::PaletteIndex::DocComment; else if (docComment->global) - m_tokenColors[token] = TextEditor::PaletteIndex::GlobalDocComment; + m_tokenColors[token] = ui::TextEditor::PaletteIndex::GlobalDocComment; else - m_tokenColors[token] = TextEditor::PaletteIndex::DocBlockComment; + m_tokenColors[token] = ui::TextEditor::PaletteIndex::DocBlockComment; } } else if (token->type == Token::Type::Comment) { auto comment = getValue(0); @@ -1438,9 +1438,9 @@ namespace hex::plugin::builtin { if (comment != nullptr && !m_tokenColors.contains(token)) { if (comment->singleLine) - m_tokenColors[token] = TextEditor::PaletteIndex::Comment; + m_tokenColors[token] = ui::TextEditor::PaletteIndex::Comment; else - m_tokenColors[token] = TextEditor::PaletteIndex::BlockComment; + m_tokenColors[token] = ui::TextEditor::PaletteIndex::BlockComment; } } else setIdentifierColor(tokenId, type); @@ -1499,7 +1499,7 @@ namespace hex::plugin::builtin { identifierType = identifier->getType(); std::string variableName = identifier->get(); - if (m_tokenColors.contains(token) && (m_tokenColors.at(token) != TextEditor::PaletteIndex::Default && identifierType != IdentifierType::Unknown)) { + if (m_tokenColors.contains(token) && (m_tokenColors.at(token) != ui::TextEditor::PaletteIndex::Default && identifierType != IdentifierType::Unknown)) { next(); continue; } diff --git a/plugins/builtin/source/content/themes.cpp b/plugins/builtin/source/content/themes.cpp index 8a4c9d61f..f9882b4b5 100644 --- a/plugins/builtin/source/content/themes.cpp +++ b/plugins/builtin/source/content/themes.cpp @@ -213,61 +213,61 @@ namespace hex::plugin::builtin { } { const static ThemeManager::ColorMap TextEditorColorMap = { - { "attribute", u32(TextEditor::PaletteIndex::Attribute) }, - { "background", u32(TextEditor::PaletteIndex::Background) }, - { "breakpoint", u32(TextEditor::PaletteIndex::Breakpoint) }, - { "calculated-pointer", u32(TextEditor::PaletteIndex::CalculatedPointer) }, - { "char-literal", u32(TextEditor::PaletteIndex::CharLiteral) }, - { "comment", u32(TextEditor::PaletteIndex::Comment) }, - { "current-line-edge", u32(TextEditor::PaletteIndex::CurrentLineEdge) }, - { "current-line-fill", u32(TextEditor::PaletteIndex::CurrentLineFill) }, - { "current-line-fill-inactive", u32(TextEditor::PaletteIndex::CurrentLineFillInactive) }, - { "cursor", u32(TextEditor::PaletteIndex::Cursor) }, - { "debug-text", u32(TextEditor::PaletteIndex::DebugText) }, - { "default", u32(TextEditor::PaletteIndex::Default) }, - { "default-text", u32(TextEditor::PaletteIndex::DefaultText) }, - { "doc-block-comment", u32(TextEditor::PaletteIndex::DocBlockComment) }, - { "doc-comment", u32(TextEditor::PaletteIndex::DocComment) }, - { "doc-global-comment", u32(TextEditor::PaletteIndex::GlobalDocComment) }, - { "error-marker", u32(TextEditor::PaletteIndex::ErrorMarker) }, - { "error-text", u32(TextEditor::PaletteIndex::ErrorText) }, - { "function", u32(TextEditor::PaletteIndex::Function) }, - { "function-parameter", u32(TextEditor::PaletteIndex::FunctionParameter) }, - { "function-variable", u32(TextEditor::PaletteIndex::FunctionVariable) }, - { "global-variable", u32(TextEditor::PaletteIndex::GlobalVariable) }, - { "identifier" , u32(TextEditor::PaletteIndex::Identifier) }, - { "keyword", u32(TextEditor::PaletteIndex::Keyword) }, - { "known-identifier", u32(TextEditor::PaletteIndex::BuiltInType) }, - { "line-number", u32(TextEditor::PaletteIndex::LineNumber) }, - { "local-variable", u32(TextEditor::PaletteIndex::LocalVariable) }, - { "multi-line-comment", u32(TextEditor::PaletteIndex::BlockComment) }, - { "namespace", u32(TextEditor::PaletteIndex::NameSpace) }, - { "number", u32(TextEditor::PaletteIndex::NumericLiteral) }, - { "pattern-variable", u32(TextEditor::PaletteIndex::PatternVariable) }, - { "placed-variable", u32(TextEditor::PaletteIndex::PlacedVariable) }, - { "preprocessor", u32(TextEditor::PaletteIndex::Directive) }, - { "preprocessor-deactivated", u32(TextEditor::PaletteIndex::PreprocessorDeactivated) }, - { "preproc-identifier", u32(TextEditor::PaletteIndex::PreprocIdentifier) }, - { "punctuation", u32(TextEditor::PaletteIndex::Operator) }, - { "selection", u32(TextEditor::PaletteIndex::Selection) }, - { "separator", u32(TextEditor::PaletteIndex::Separator) }, - { "string", u32(TextEditor::PaletteIndex::StringLiteral) }, - { "template-variable", u32(TextEditor::PaletteIndex::TemplateArgument) }, - { "typedef", u32(TextEditor::PaletteIndex::TypeDef) }, - { "unknown-identifier", u32(TextEditor::PaletteIndex::UnkIdentifier) }, - { "user-defined-type", u32(TextEditor::PaletteIndex::UserDefinedType) }, - { "view", u32(TextEditor::PaletteIndex::View) }, - { "warning-text", u32(TextEditor::PaletteIndex::WarningText) } + { "attribute", u32(ui::TextEditor::PaletteIndex::Attribute) }, + { "background", u32(ui::TextEditor::PaletteIndex::Background) }, + { "breakpoint", u32(ui::TextEditor::PaletteIndex::Breakpoint) }, + { "calculated-pointer", u32(ui::TextEditor::PaletteIndex::CalculatedPointer) }, + { "char-literal", u32(ui::TextEditor::PaletteIndex::CharLiteral) }, + { "comment", u32(ui::TextEditor::PaletteIndex::Comment) }, + { "current-line-edge", u32(ui::TextEditor::PaletteIndex::CurrentLineEdge) }, + { "current-line-fill", u32(ui::TextEditor::PaletteIndex::CurrentLineFill) }, + { "current-line-fill-inactive", u32(ui::TextEditor::PaletteIndex::CurrentLineFillInactive) }, + { "cursor", u32(ui::TextEditor::PaletteIndex::Cursor) }, + { "debug-text", u32(ui::TextEditor::PaletteIndex::DebugText) }, + { "default", u32(ui::TextEditor::PaletteIndex::Default) }, + { "default-text", u32(ui::TextEditor::PaletteIndex::DefaultText) }, + { "doc-block-comment", u32(ui::TextEditor::PaletteIndex::DocBlockComment) }, + { "doc-comment", u32(ui::TextEditor::PaletteIndex::DocComment) }, + { "doc-global-comment", u32(ui::TextEditor::PaletteIndex::GlobalDocComment) }, + { "error-marker", u32(ui::TextEditor::PaletteIndex::ErrorMarker) }, + { "error-text", u32(ui::TextEditor::PaletteIndex::ErrorText) }, + { "function", u32(ui::TextEditor::PaletteIndex::Function) }, + { "function-parameter", u32(ui::TextEditor::PaletteIndex::FunctionParameter) }, + { "function-variable", u32(ui::TextEditor::PaletteIndex::FunctionVariable) }, + { "global-variable", u32(ui::TextEditor::PaletteIndex::GlobalVariable) }, + { "identifier" , u32(ui::TextEditor::PaletteIndex::Identifier) }, + { "keyword", u32(ui::TextEditor::PaletteIndex::Keyword) }, + { "known-identifier", u32(ui::TextEditor::PaletteIndex::BuiltInType) }, + { "line-number", u32(ui::TextEditor::PaletteIndex::LineNumber) }, + { "local-variable", u32(ui::TextEditor::PaletteIndex::LocalVariable) }, + { "multi-line-comment", u32(ui::TextEditor::PaletteIndex::BlockComment) }, + { "namespace", u32(ui::TextEditor::PaletteIndex::NameSpace) }, + { "number", u32(ui::TextEditor::PaletteIndex::NumericLiteral) }, + { "pattern-variable", u32(ui::TextEditor::PaletteIndex::PatternVariable) }, + { "placed-variable", u32(ui::TextEditor::PaletteIndex::PlacedVariable) }, + { "preprocessor", u32(ui::TextEditor::PaletteIndex::Directive) }, + { "preprocessor-deactivated", u32(ui::TextEditor::PaletteIndex::PreprocessorDeactivated) }, + { "preproc-identifier", u32(ui::TextEditor::PaletteIndex::PreprocIdentifier) }, + { "punctuation", u32(ui::TextEditor::PaletteIndex::Operator) }, + { "selection", u32(ui::TextEditor::PaletteIndex::Selection) }, + { "separator", u32(ui::TextEditor::PaletteIndex::Separator) }, + { "string", u32(ui::TextEditor::PaletteIndex::StringLiteral) }, + { "template-variable", u32(ui::TextEditor::PaletteIndex::TemplateArgument) }, + { "typedef", u32(ui::TextEditor::PaletteIndex::TypeDef) }, + { "unknown-identifier", u32(ui::TextEditor::PaletteIndex::UnkIdentifier) }, + { "user-defined-type", u32(ui::TextEditor::PaletteIndex::UserDefinedType) }, + { "view", u32(ui::TextEditor::PaletteIndex::View) }, + { "warning-text", u32(ui::TextEditor::PaletteIndex::WarningText) } }; ThemeManager::addThemeHandler("text-editor", TextEditorColorMap, [](u32 colorId) -> ImColor { - return TextEditor::getPalette()[colorId]; + return ui::TextEditor::getPalette()[colorId]; }, [](u32 colorId, ImColor color) { - auto palette = TextEditor::getPalette(); + auto palette = ui::TextEditor::getPalette(); palette[colorId] = color; - TextEditor::setPalette(palette); + ui::TextEditor::setPalette(palette); } ); } diff --git a/plugins/builtin/source/content/tools/demangler.cpp b/plugins/builtin/source/content/tools/demangler.cpp index 0ce64a421..dd0ae6be0 100644 --- a/plugins/builtin/source/content/tools/demangler.cpp +++ b/plugins/builtin/source/content/tools/demangler.cpp @@ -11,15 +11,15 @@ namespace hex::plugin::builtin { void drawDemangler() { static std::string mangledName, demangledName, wrappedDemangledName; - static TextEditor outputField = []{ - TextEditor editor; + static ui::TextEditor outputField = []{ + ui::TextEditor editor; editor.setReadOnly(true); editor.setShowLineNumbers(false); editor.setShowWhitespaces(false); editor.setShowCursor(false); editor.setImGuiChildIgnored(true); - auto languageDef = TextEditor::LanguageDefinition::CPlusPlus(); + auto languageDef = ui::TextEditor::LanguageDefinition::CPlusPlus(); for (auto &[name, identifier] : languageDef.m_identifiers) identifier.m_declaration = ""; diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 00c49cdc6..262bcde63 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -44,9 +44,9 @@ namespace hex::plugin::builtin { using namespace hex::literals; - static const TextEditor::LanguageDefinition &PatternLanguage() { + static const ui::TextEditor::LanguageDefinition &PatternLanguage() { static bool initialized = false; - static TextEditor::LanguageDefinition langDef; + static ui::TextEditor::LanguageDefinition langDef; if (!initialized) { constexpr static std::array keywords = { "using", "struct", "union", "enum", "bitfield", "be", "le", "if", "else", "match", "false", "true", "this", "parent", "addressof", "sizeof", "typenameof", "while", "for", "fn", "return", "break", "continue", "namespace", "in", "out", "ref", "null", "const", "unsigned", "signed", "try", "catch", "import", "as", "from" @@ -58,7 +58,7 @@ namespace hex::plugin::builtin { "u8", "u16", "u24", "u32", "u48", "u64", "u96", "u128", "s8", "s16", "s24", "s32", "s48", "s64", "s96", "s128", "float", "double", "char", "char16", "bool", "padding", "str", "auto" }; for (const auto name : builtInTypes) { - TextEditor::Identifier id; + ui::TextEditor::Identifier id; id.m_declaration = ""; langDef.m_identifiers.insert(std::make_pair(std::string(name), id)); } @@ -66,12 +66,12 @@ namespace hex::plugin::builtin { "include", "define", "ifdef", "ifndef", "endif", "undef", "pragma", "error" }; for (const auto name : directives) { - TextEditor::Identifier id; + ui::TextEditor::Identifier id; id.m_declaration = ""; langDef.m_preprocIdentifiers.insert(std::make_pair(std::string(name), id)); } - langDef.m_tokenize = [](std::string::const_iterator inBegin, std::string::const_iterator inEnd, std::string::const_iterator &outBegin, std::string::const_iterator &outEnd, TextEditor::PaletteIndex &paletteIndex) -> bool { - paletteIndex = TextEditor::PaletteIndex::Max; + langDef.m_tokenize = [](std::string::const_iterator inBegin, std::string::const_iterator inEnd, std::string::const_iterator &outBegin, std::string::const_iterator &outEnd, ui::TextEditor::PaletteIndex &paletteIndex) -> bool { + paletteIndex = ui::TextEditor::PaletteIndex::Max; while (inBegin < inEnd && isascii(*inBegin) && std::isblank(*inBegin)) ++inBegin; @@ -79,21 +79,21 @@ namespace hex::plugin::builtin { if (inBegin == inEnd) { outBegin = inEnd; outEnd = inEnd; - paletteIndex = TextEditor::PaletteIndex::Default; - } else if (tokenizeCStyleIdentifier(inBegin, inEnd, outBegin, outEnd)) { - paletteIndex = TextEditor::PaletteIndex::Identifier; - } else if (tokenizeCStyleNumber(inBegin, inEnd, outBegin, outEnd)) { - paletteIndex = TextEditor::PaletteIndex::NumericLiteral; - } else if (tokenizeCStyleCharacterLiteral(inBegin, inEnd, outBegin, outEnd)) { - paletteIndex = TextEditor::PaletteIndex::CharLiteral; - } else if (tokenizeCStyleString(inBegin, inEnd, outBegin, outEnd)) { - paletteIndex = TextEditor::PaletteIndex::StringLiteral; - } else if (tokenizeCStyleSeparator(inBegin, inEnd, outBegin, outEnd)) { - paletteIndex = TextEditor::PaletteIndex::Separator; - } else if (tokenizeCStyleOperator(inBegin, inEnd, outBegin, outEnd)) { - paletteIndex = TextEditor::PaletteIndex::Operator; + paletteIndex = ui::TextEditor::PaletteIndex::Default; + } else if (ui::tokenizeCStyleIdentifier(inBegin, inEnd, outBegin, outEnd)) { + paletteIndex = ui::TextEditor::PaletteIndex::Identifier; + } else if (ui::tokenizeCStyleNumber(inBegin, inEnd, outBegin, outEnd)) { + paletteIndex = ui::TextEditor::PaletteIndex::NumericLiteral; + } else if (ui::tokenizeCStyleCharacterLiteral(inBegin, inEnd, outBegin, outEnd)) { + paletteIndex = ui::TextEditor::PaletteIndex::CharLiteral; + } else if (ui::tokenizeCStyleString(inBegin, inEnd, outBegin, outEnd)) { + paletteIndex = ui::TextEditor::PaletteIndex::StringLiteral; + } else if (ui::tokenizeCStyleSeparator(inBegin, inEnd, outBegin, outEnd)) { + paletteIndex = ui::TextEditor::PaletteIndex::Separator; + } else if (ui::tokenizeCStyleOperator(inBegin, inEnd, outBegin, outEnd)) { + paletteIndex = ui::TextEditor::PaletteIndex::Operator; } - return paletteIndex != TextEditor::PaletteIndex::Max; + return paletteIndex != ui::TextEditor::PaletteIndex::Max; }; langDef.m_commentStart = "/*"; @@ -116,22 +116,22 @@ namespace hex::plugin::builtin { return langDef; } - static const TextEditor::LanguageDefinition &ConsoleLog() { + static const ui::TextEditor::LanguageDefinition &ConsoleLog() { static bool initialized = false; - static TextEditor::LanguageDefinition langDef; + static ui::TextEditor::LanguageDefinition langDef; if (!initialized) { - langDef.m_tokenize = [](std::string::const_iterator inBegin, std::string::const_iterator inEnd, std::string::const_iterator &outBegin, std::string::const_iterator &outEnd, TextEditor::PaletteIndex &paletteIndex) -> bool { + langDef.m_tokenize = [](std::string::const_iterator inBegin, std::string::const_iterator inEnd, std::string::const_iterator &outBegin, std::string::const_iterator &outEnd, ui::TextEditor::PaletteIndex &paletteIndex) -> bool { std::string_view inView(inBegin, inEnd); if (inView.starts_with("D: ")) - paletteIndex = TextEditor::PaletteIndex::DefaultText; + paletteIndex = ui::TextEditor::PaletteIndex::DefaultText; else if (inView.starts_with("I: ")) - paletteIndex = TextEditor::PaletteIndex::DebugText; + paletteIndex = ui::TextEditor::PaletteIndex::DebugText; else if (inView.starts_with("W: ")) - paletteIndex = TextEditor::PaletteIndex::WarningText; + paletteIndex = ui::TextEditor::PaletteIndex::WarningText; else if (inView.starts_with("E: ")) - paletteIndex = TextEditor::PaletteIndex::ErrorText; + paletteIndex = ui::TextEditor::PaletteIndex::ErrorText; else - paletteIndex = TextEditor::PaletteIndex::Max; + paletteIndex = ui::TextEditor::PaletteIndex::Max; outBegin = inBegin; outEnd = inEnd; @@ -241,7 +241,7 @@ namespace hex::plugin::builtin { RequestAddVirtualFile::unsubscribe(this); } - void ViewPatternEditor::setupFindReplace(TextEditor *editor) { + void ViewPatternEditor::setupFindReplace(ui::TextEditor *editor) { if (editor == nullptr) return; if (m_openFindReplacePopUp) { @@ -280,7 +280,7 @@ namespace hex::plugin::builtin { } - void ViewPatternEditor::setupGotoLine(TextEditor *editor) { + void ViewPatternEditor::setupGotoLine(ui::TextEditor *editor) { // Context menu entries that open the goto line popup if (m_openGotoLinePopUp) { @@ -614,7 +614,7 @@ namespace hex::plugin::builtin { } } - void ViewPatternEditor::drawTextEditorFindReplacePopup(TextEditor *textEditor) { + void ViewPatternEditor::drawTextEditorFindReplacePopup(ui::TextEditor *textEditor) { auto provider = ImHexApi::Provider::get(); ImGuiWindowFlags popupFlags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar; if (ImGui::BeginPopup("##text_editor_view_find_replace_popup", popupFlags)) { @@ -624,7 +624,7 @@ namespace hex::plugin::builtin { static u64 count = 0; static bool updateCount = false; static bool canReplace = true; - TextEditor::FindReplaceHandler *findReplaceHandler = textEditor->getFindReplaceHandler(); + ui::TextEditor::FindReplaceHandler *findReplaceHandler = textEditor->getFindReplaceHandler(); if (ImGui::IsWindowAppearing()) { findReplaceHandler->resetMatches(); @@ -940,7 +940,7 @@ namespace hex::plugin::builtin { } } - void ViewPatternEditor::drawTextEditorGotoLinePopup(TextEditor *textEditor) { + void ViewPatternEditor::drawTextEditorGotoLinePopup(ui::TextEditor *textEditor) { auto provider = ImHexApi::Provider::get(); ImGuiWindowFlags popupFlags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar; if (ImGui::BeginPopup("##text_editor_view_goto_line_popup", popupFlags)) { @@ -1320,7 +1320,7 @@ namespace hex::plugin::builtin { return wolv::util::combineStrings(lines, "\n"); }; - TextEditor::ErrorMarkers errorMarkers; + ui::TextEditor::ErrorMarkers errorMarkers; if (*m_callStack != nullptr && !(*m_callStack)->empty()) { for (const auto &frame : **m_callStack | std::views::reverse) { auto location = frame.node->getLocation(); @@ -1328,13 +1328,13 @@ namespace hex::plugin::builtin { std::string message = ""; if (m_lastEvaluationError->has_value()) message = processMessage((*m_lastEvaluationError)->message); - auto key = TextEditor::Coordinates(location.line, location.column); + auto key = ui::TextEditor::Coordinates(location.line, location.column); errorMarkers[key] = std::make_pair(u32(location.length), message); } } } else { if (m_lastEvaluationError->has_value()) { - auto key = TextEditor::Coordinates((*m_lastEvaluationError)->line, 0); + auto key = ui::TextEditor::Coordinates((*m_lastEvaluationError)->line, 0); errorMarkers[key] = std::make_pair(0,processMessage((*m_lastEvaluationError)->message)); } } @@ -1343,7 +1343,7 @@ namespace hex::plugin::builtin { for (const auto &error : *m_lastCompileError) { auto source = error.getLocation().source; if (source != nullptr && source->mainSource) { - auto key = TextEditor::Coordinates(error.getLocation().line, error.getLocation().column); + auto key = ui::TextEditor::Coordinates(error.getLocation().line, error.getLocation().column); if (!errorMarkers.contains(key) ||errorMarkers[key].first < error.getLocation().length) errorMarkers[key] = std::make_pair(u32(error.getLocation().length), processMessage(error.getMessage())); } @@ -1527,7 +1527,7 @@ namespace hex::plugin::builtin { const bool shiftHeld = ImGui::GetIO().KeyShift; ImGui::ColorButton(pattern->getVariableName().c_str(), ImColor(pattern->getColor()), ImGuiColorEditFlags_AlphaOpaque); ImGui::SameLine(0, 10); - ImGuiExt::TextFormattedColored(TextEditor::getPalette()[u32(TextEditor::PaletteIndex::BuiltInType)], "{} ", pattern->getFormattedName()); + ImGuiExt::TextFormattedColored(ui::TextEditor::getPalette()[u32(ui::TextEditor::PaletteIndex::BuiltInType)], "{} ", pattern->getFormattedName()); ImGui::SameLine(0, 5); ImGuiExt::TextFormatted("{}", pattern->getDisplayName()); ImGui::SameLine(); @@ -1846,7 +1846,7 @@ namespace hex::plugin::builtin { if (line == 0) return; - const TextEditor::Coordinates coords = { int(line) - 1, int(column) }; + const ui::TextEditor::Coordinates coords = { int(line) - 1, int(column) }; m_textEditor.get(provider).setCursorPosition(coords); }); @@ -1900,7 +1900,7 @@ namespace hex::plugin::builtin { m_envVarEntries.get(provider).emplace_back(0, "", i128(0), EnvVarType::Integer); m_debuggerDrawer.get(provider) = std::make_unique(); - m_cursorPosition.get(provider) = TextEditor::Coordinates(0, 0); + m_cursorPosition.get(provider) = ui::TextEditor::Coordinates(0, 0); }); EventProviderChanged::subscribe(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) { @@ -1919,7 +1919,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)); - TextEditor::Selection selection = m_selection.get(newProvider); + ui::TextEditor::Selection 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")); @@ -1976,7 +1976,7 @@ namespace hex::plugin::builtin { appendEditorText(fmt::format("{0} {0}_array_at_0x{1:02X}[0x{2:02X}] @ 0x{1:02X};", type, selection->getStartAddress(), (selection->getSize() + (size - 1)) / size)); } - TextEditor *ViewPatternEditor::getEditorFromFocusedWindow() { + ui::TextEditor *ViewPatternEditor::getEditorFromFocusedWindow() { auto provider = ImHexApi::Provider::get(); if (provider != nullptr) { if (m_focusedSubWindowName.contains(consoleView)) { @@ -2030,7 +2030,7 @@ namespace hex::plugin::builtin { /* Find Next */ ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.view.pattern_editor.menu.find_next" }, 1520, AllowWhileTyping + Keys::F3, [this] { if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) { - TextEditor::FindReplaceHandler *findReplaceHandler = editor->getFindReplaceHandler(); + ui::TextEditor::FindReplaceHandler *findReplaceHandler = editor->getFindReplaceHandler(); findReplaceHandler->findMatch(editor, true); } else { m_textEditor->getFindReplaceHandler()->findMatch(&*m_textEditor, true); @@ -2048,7 +2048,7 @@ namespace hex::plugin::builtin { /* Find Previous */ ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.view.pattern_editor.menu.find_previous" }, 1530, AllowWhileTyping + SHIFT + Keys::F3, [this] { if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) { - TextEditor::FindReplaceHandler *findReplaceHandler = editor->getFindReplaceHandler(); + ui::TextEditor::FindReplaceHandler *findReplaceHandler = editor->getFindReplaceHandler(); findReplaceHandler->findMatch(editor, false); } else { m_textEditor->getFindReplaceHandler()->findMatch(&*m_textEditor, false); @@ -2392,21 +2392,21 @@ namespace hex::plugin::builtin { ShortcutManager::addShortcut(this, CTRL + SHIFT + Keys::C + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.match_case_toggle", [this] { if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) { - TextEditor::FindReplaceHandler *findReplaceHandler = editor->getFindReplaceHandler(); + ui::TextEditor::FindReplaceHandler *findReplaceHandler = editor->getFindReplaceHandler(); findReplaceHandler->setMatchCase(editor, !findReplaceHandler->getMatchCase()); } }); ShortcutManager::addShortcut(this, CTRL + SHIFT + Keys::R + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.regex_toggle", [this] { if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) { - TextEditor::FindReplaceHandler *findReplaceHandler = editor->getFindReplaceHandler(); + ui::TextEditor::FindReplaceHandler *findReplaceHandler = editor->getFindReplaceHandler(); findReplaceHandler->setFindRegEx(editor, !findReplaceHandler->getFindRegEx()); } }); ShortcutManager::addShortcut(this, CTRL + SHIFT + Keys::W + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.whole_word_toggle", [this] { if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) { - TextEditor::FindReplaceHandler *findReplaceHandler = editor->getFindReplaceHandler(); + ui::TextEditor::FindReplaceHandler *findReplaceHandler = editor->getFindReplaceHandler(); findReplaceHandler->setWholeWord(editor, !findReplaceHandler->getWholeWord()); } });