#pragma once #include #include #include #include #include #include #include #include #include "imgui.h" class CTextEditor { public: enum class PaletteIndex { Default, Keyword, Number, String, CharLiteral, Punctuation, Preprocessor, Identifier, KnownIdentifier, PreprocIdentifier, Comment, MultiLineComment, Background, Cursor, Selection, ErrorMarker, Breakpoint, LineNumber, CurrentLineFill, CurrentLineFillInactive, CurrentLineEdge, Max }; enum class SelectionMode { Normal, Word, Line }; struct Breakpoint { int mLine; bool mEnabled; std::string mCondition; Breakpoint() : mLine(-1) , mEnabled(false) {} }; // Represents a character coordinate from the user's point of view, // i. e. consider an uniform grid (assuming fixed-width font) on the // screen as it is rendered, and each cell has its own coordinate, starting from 0. // Tabs are counted as [1..mTabSize] count empty spaces, depending on // how many space is necessary to reach the next tab stop. // For example, coordinate (1, 5) represents the character 'B' in a line "\tABC", when mTabSize = 4, // because it is rendered as " ABC" on the screen. struct Coordinates { Coordinates() : m_nLine(0), m_nColumn(0) {} Coordinates(int aLine, int aColumn) : m_nLine(aLine), m_nColumn(aColumn) { assert(aLine >= 0); assert(aColumn >= 0); } static Coordinates Invalid() { static Coordinates invalid(-1, -1); return invalid; } bool operator ==(const Coordinates& o) const { return m_nLine == o.m_nLine && m_nColumn == o.m_nColumn; } bool operator !=(const Coordinates& o) const { return m_nLine != o.m_nLine || m_nColumn != o.m_nColumn; } bool operator <(const Coordinates& o) const { if (m_nLine != o.m_nLine) return m_nLine < o.m_nLine; return m_nColumn < o.m_nColumn; } bool operator >(const Coordinates& o) const { if (m_nLine != o.m_nLine) return m_nLine > o.m_nLine; return m_nColumn > o.m_nColumn; } bool operator <=(const Coordinates& o) const { if (m_nLine != o.m_nLine) return m_nLine < o.m_nLine; return m_nColumn <= o.m_nColumn; } bool operator >=(const Coordinates& o) const { if (m_nLine != o.m_nLine) return m_nLine > o.m_nLine; return m_nColumn >= o.m_nColumn; } int m_nLine, m_nColumn; }; struct Identifier { Coordinates m_Location; std::string m_svDeclaration; }; typedef std::string String; typedef std::unordered_map Identifiers; typedef std::unordered_set Keywords; typedef std::map ErrorMarkers; typedef std::unordered_set Breakpoints; typedef std::array Palette; typedef uint8_t Char; struct Glyph { Char m_Char; PaletteIndex m_ColorIndex = PaletteIndex::Default; bool m_bComment : 1; bool m_bMultiLineComment : 1; bool m_bPreprocessor : 1; Glyph(Char aChar, PaletteIndex aColorIndex) : m_Char(aChar), m_ColorIndex(aColorIndex), m_bComment(false), m_bMultiLineComment(false), m_bPreprocessor(false) {} }; typedef std::vector Line; typedef std::vector Lines; struct LanguageDefinition { typedef std::pair TokenRegexString; typedef std::vector TokenRegexStrings; typedef bool(*TokenizeCallback)(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end, PaletteIndex & paletteIndex); std::string m_svName; Keywords m_Keywords; Identifiers m_Identifiers; Identifiers m_PreprocIdentifiers; std::string m_svCommentStart, m_svCommentEnd, m_svSingleLineComment; char m_PreprocChar; bool m_bAutoIndentation; TokenizeCallback m_Tokenize; TokenRegexStrings m_TokenRegexStrings; bool mCaseSensitive; LanguageDefinition() : m_PreprocChar('#'), m_bAutoIndentation(true), m_Tokenize(nullptr), mCaseSensitive(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(); }; CTextEditor(); ~CTextEditor(); void SetLanguageDefinition(const LanguageDefinition& aLanguageDef); const LanguageDefinition& GetLanguageDefinition() const { return m_LanguageDefinition; } const Palette& GetPalette() const { return m_PaletteBase; } void SetPalette(const Palette& aValue); void SetErrorMarkers(const ErrorMarkers& aMarkers) { m_ErrorMarkers = aMarkers; } void SetBreakpoints(const Breakpoints& aMarkers) { m_Breakpoints = aMarkers; } void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); void SetText(const std::string& aText); std::string GetText() const; void SetTextLines(const std::vector& aLines); std::vector GetTextLines() const; std::string GetSelectedText() const; std::string GetCurrentLineText()const; int GetTotalLines() const { return (int)m_Lines.size(); } bool IsOverwrite() const { return m_Overwrite; } void SetReadOnly(bool aValue); bool IsReadOnly() const { return m_bReadOnly; } bool IsTextChanged() const { return m_bTextChanged; } bool IsCursorPositionChanged() const { return m_bCursorPositionChanged; } bool IsColorizerEnabled() const { return m_bColorizerEnabled; } void SetColorizerEnable(bool aValue); Coordinates GetCursorPosition() const { return GetActualCursorCoordinates(); } void SetCursorPosition(const Coordinates& aPosition); inline void SetHandleMouseInputs (bool aValue){ m_bHandleMouseInputs = aValue;} inline bool IsHandleMouseInputsEnabled() const { return m_bHandleMouseInputs; } inline void SetHandleKeyboardInputs (bool aValue){ m_bHandleKeyboardInputs = aValue;} inline bool IsHandleKeyboardInputsEnabled() const { return m_bHandleKeyboardInputs; } inline void SetImGuiChildIgnored (bool aValue){ m_bIgnoreImGuiChild = aValue;} inline bool IsImGuiChildIgnored() const { return m_bIgnoreImGuiChild; } inline void SetShowWhitespaces(bool aValue) { m_bShowWhitespaces = aValue; } inline bool IsShowingWhitespaces() const { return m_bShowWhitespaces; } void SetTabSize(int aValue); inline int GetTabSize() const { return m_nTabSize; } void InsertText(const std::string& aValue); void InsertText(const char* aValue); void MoveUp(int aAmount = 1, bool aSelect = false); void MoveDown(int aAmount = 1, bool aSelect = false); void MoveLeft(int aAmount = 1, bool aSelect = false, bool aWordMode = false); void MoveRight(int aAmount = 1, bool aSelect = false, bool aWordMode = false); void MoveTop(bool aSelect = false); void MoveBottom(bool aSelect = false); void MoveHome(bool aSelect = false); void MoveEnd(bool aSelect = false); void SetSelectionStart(const Coordinates& aPosition); void SetSelectionEnd(const Coordinates& aPosition); void SetSelection(const Coordinates& aStart, const Coordinates& aEnd, SelectionMode aMode = SelectionMode::Normal); void SelectWordUnderCursor(); void SelectAll(); bool HasSelection() const; void Copy(); void Cut(); void Paste(); void Delete(); bool CanUndo() const; bool CanRedo() const; void Undo(int aSteps = 1); void Redo(int aSteps = 1); static const Palette& GetDarkPalette(); static const Palette& GetLightPalette(); static const Palette& GetRetroBluePalette(); private: typedef std::vector> RegexList; struct EditorState { Coordinates m_SelectionStart; Coordinates m_SelectionEnd; Coordinates m_CursorPosition; }; class UndoRecord { public: UndoRecord() {} ~UndoRecord() {} UndoRecord( const std::string& aAdded, const CTextEditor::Coordinates aAddedStart, const CTextEditor::Coordinates aAddedEnd, const std::string& aRemoved, const CTextEditor::Coordinates aRemovedStart, const CTextEditor::Coordinates aRemovedEnd, CTextEditor::EditorState& aBefore, CTextEditor::EditorState& aAfter); void Undo(CTextEditor* aEditor); void Redo(CTextEditor* aEditor); std::string m_svAdded; Coordinates m_AddedStart; Coordinates m_AddedEnd; std::string m_svRemoved; Coordinates m_RemovedStart; Coordinates m_RemovedEnd; EditorState m_Before; EditorState m_After; }; typedef std::vector UndoBuffer; void ProcessInputs(); void Colorize(int aFromLine = 0, int aCount = -1); void ColorizeRange(int aFromLine = 0, int aToLine = 0); void ColorizeInternal(); float TextDistanceToLineStart(const Coordinates& aFrom) const; void EnsureCursorVisible(); int GetPageSize() const; std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const; Coordinates GetActualCursorCoordinates() const; Coordinates SanitizeCoordinates(const Coordinates& aValue) const; void Advance(Coordinates& aCoordinates) const; void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd); int InsertTextAt(Coordinates& aWhere, const char* aValue); void AddUndo(UndoRecord& aValue); Coordinates ScreenPosToCoordinates(const ImVec2& aPosition) const; Coordinates FindWordStart(const Coordinates& aFrom) const; Coordinates FindWordEnd(const Coordinates& aFrom) const; Coordinates FindNextWord(const Coordinates& aFrom) const; int GetCharacterIndex(const Coordinates& aCoordinates) const; int GetCharacterColumn(int aLine, int aIndex) const; int GetLineCharacterCount(int aLine) const; int GetLineMaxColumn(int aLine) const; bool IsOnWordBoundary(const Coordinates& aAt) const; void RemoveLine(int aStart, int aEnd); void RemoveLine(int aIndex); Line& InsertLine(int aIndex); void EnterCharacter(ImWchar aChar, bool aShift); void Backspace(); void DeleteSelection(); std::string GetWordUnderCursor() const; std::string GetWordAt(const Coordinates& aCoords) const; ImU32 GetGlyphColor(const Glyph& aGlyph) const; void HandleKeyboardInputs(); void HandleMouseInputs(); void Render(); float m_flLineSpacing; Lines m_Lines; EditorState m_State; UndoBuffer m_UndoBuffer; int m_nUndoIndex; int m_nTabSize; bool m_Overwrite; bool m_bReadOnly; bool m_bWithinRender; bool m_bScrollToCursor; bool m_bScrollToTop; bool m_bTextChanged; bool m_bColorizerEnabled; float m_flTextStart; // position (in pixels) where a code line starts relative to the left of the TextEditor. int m_nLeftMargin; bool m_bCursorPositionChanged; int m_nColorRangeMin; int m_nColorRangeMax; SelectionMode m_SelectionMode; bool m_bHandleKeyboardInputs; bool m_bHandleMouseInputs; bool m_bIgnoreImGuiChild; bool m_bShowWhitespaces; Palette m_PaletteBase; Palette m_Palette; LanguageDefinition m_LanguageDefinition; RegexList m_RegexList; bool m_bCheckComments; Breakpoints m_Breakpoints; ErrorMarkers m_ErrorMarkers; ImVec2 m_CharAdvance; Coordinates m_InteractiveStart; Coordinates m_InteractiveEnd; std::string m__svLineBuffer; uint64_t m_nStartTime; float m_flLastClick; };