1 #pragma once 2 3 #include <string> 4 #include <vector> 5 #include <array> 6 #include <memory> 7 #include <unordered_set> 8 #include <unordered_map> 9 #include <map> 10 #include <regex> 11 #include "imgui.h" 12 13 class TextEditor 14 { 15 public: 16 enum class PaletteIndex 17 { 18 Default, 19 Keyword, 20 Number, 21 String, 22 CharLiteral, 23 Punctuation, 24 Preprocessor, 25 Identifier, 26 KnownIdentifier, 27 PreprocIdentifier, 28 Comment, 29 MultiLineComment, 30 Background, 31 Cursor, 32 Selection, 33 ErrorMarker, 34 Breakpoint, 35 LineNumber, 36 CurrentLineFill, 37 CurrentLineFillInactive, 38 CurrentLineEdge, 39 Max 40 }; 41 42 enum class SelectionMode 43 { 44 Normal, 45 Word, 46 Line 47 }; 48 49 struct Breakpoint 50 { 51 int mLine; 52 bool mEnabled; 53 std::string mCondition; 54 BreakpointBreakpoint55 Breakpoint() 56 : mLine(-1) 57 , mEnabled(false) 58 {} 59 }; 60 61 // Represents a character coordinate from the user's point of view, 62 // i. e. consider an uniform grid (assuming fixed-width font) on the 63 // screen as it is rendered, and each cell has its own coordinate, starting from 0. 64 // Tabs are counted as [1..mTabSize] count empty spaces, depending on 65 // how many space is necessary to reach the next tab stop. 66 // For example, coordinate (1, 5) represents the character 'B' in a line "\tABC", when mTabSize = 4, 67 // because it is rendered as " ABC" on the screen. 68 struct Coordinates 69 { 70 int mLine, mColumn; CoordinatesCoordinates71 Coordinates() : mLine(0), mColumn(0) {} CoordinatesCoordinates72 Coordinates(int aLine, int aColumn) : mLine(aLine), mColumn(aColumn) 73 { 74 assert(aLine >= 0); 75 assert(aColumn >= 0); 76 } InvalidCoordinates77 static Coordinates Invalid() { static Coordinates invalid(-1, -1); return invalid; } 78 79 bool operator ==(const Coordinates& o) const 80 { 81 return 82 mLine == o.mLine && 83 mColumn == o.mColumn; 84 } 85 86 bool operator !=(const Coordinates& o) const 87 { 88 return 89 mLine != o.mLine || 90 mColumn != o.mColumn; 91 } 92 93 bool operator <(const Coordinates& o) const 94 { 95 if (mLine != o.mLine) 96 return mLine < o.mLine; 97 return mColumn < o.mColumn; 98 } 99 100 bool operator >(const Coordinates& o) const 101 { 102 if (mLine != o.mLine) 103 return mLine > o.mLine; 104 return mColumn > o.mColumn; 105 } 106 107 bool operator <=(const Coordinates& o) const 108 { 109 if (mLine != o.mLine) 110 return mLine < o.mLine; 111 return mColumn <= o.mColumn; 112 } 113 114 bool operator >=(const Coordinates& o) const 115 { 116 if (mLine != o.mLine) 117 return mLine > o.mLine; 118 return mColumn >= o.mColumn; 119 } 120 }; 121 122 struct Identifier 123 { 124 Coordinates mLocation; 125 std::string mDeclaration; 126 }; 127 128 typedef std::string String; 129 typedef std::unordered_map<std::string, Identifier> Identifiers; 130 typedef std::unordered_set<std::string> Keywords; 131 typedef std::map<int, std::string> ErrorMarkers; 132 typedef std::unordered_set<int> Breakpoints; 133 typedef std::array<ImU32, (unsigned)PaletteIndex::Max> Palette; 134 typedef uint8_t Char; 135 136 struct Glyph 137 { 138 Char mChar; 139 PaletteIndex mColorIndex = PaletteIndex::Default; 140 bool mComment : 1; 141 bool mMultiLineComment : 1; 142 bool mPreprocessor : 1; 143 GlyphGlyph144 Glyph(Char aChar, PaletteIndex aColorIndex) : mChar(aChar), mColorIndex(aColorIndex), 145 mComment(false), mMultiLineComment(false), mPreprocessor(false) {} 146 }; 147 148 typedef std::vector<Glyph> Line; 149 typedef std::vector<Line> Lines; 150 151 struct LanguageDefinition 152 { 153 typedef std::pair<std::string, PaletteIndex> TokenRegexString; 154 typedef std::vector<TokenRegexString> TokenRegexStrings; 155 typedef bool(*TokenizeCallback)(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end, PaletteIndex & paletteIndex); 156 157 std::string mName; 158 Keywords mKeywords; 159 Identifiers mIdentifiers; 160 Identifiers mPreprocIdentifiers; 161 std::string mCommentStart, mCommentEnd, mSingleLineComment; 162 char mPreprocChar; 163 bool mAutoIndentation; 164 165 TokenizeCallback mTokenize; 166 167 TokenRegexStrings mTokenRegexStrings; 168 169 bool mCaseSensitive; 170 LanguageDefinitionLanguageDefinition171 LanguageDefinition() 172 : mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) 173 { 174 } 175 176 static const LanguageDefinition& CPlusPlus(); 177 static const LanguageDefinition& HLSL(); 178 static const LanguageDefinition& GLSL(); 179 static const LanguageDefinition& C(); 180 static const LanguageDefinition& SQL(); 181 static const LanguageDefinition& AngelScript(); 182 static const LanguageDefinition& Lua(); 183 }; 184 185 TextEditor(); 186 ~TextEditor(); 187 188 void SetLanguageDefinition(const LanguageDefinition& aLanguageDef); GetLanguageDefinition()189 const LanguageDefinition& GetLanguageDefinition() const { return mLanguageDefinition; } 190 GetPalette()191 const Palette& GetPalette() const { return mPaletteBase; } 192 void SetPalette(const Palette& aValue); 193 SetErrorMarkers(const ErrorMarkers & aMarkers)194 void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; } SetBreakpoints(const Breakpoints & aMarkers)195 void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers; } 196 197 void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); 198 void SetText(const std::string& aText); 199 std::string GetText() const; 200 201 void SetTextLines(const std::vector<std::string>& aLines); 202 std::vector<std::string> GetTextLines() const; 203 204 std::string GetSelectedText() const; 205 std::string GetCurrentLineText()const; 206 GetTotalLines()207 int GetTotalLines() const { return (int)mLines.size(); } IsOverwrite()208 bool IsOverwrite() const { return mOverwrite; } 209 210 void SetReadOnly(bool aValue); IsReadOnly()211 bool IsReadOnly() const { return mReadOnly; } IsTextChanged()212 bool IsTextChanged() const { return mTextChanged; } IsCursorPositionChanged()213 bool IsCursorPositionChanged() const { return mCursorPositionChanged; } 214 IsColorizerEnabled()215 bool IsColorizerEnabled() const { return mColorizerEnabled; } 216 void SetColorizerEnable(bool aValue); 217 GetCursorPosition()218 Coordinates GetCursorPosition() const { return GetActualCursorCoordinates(); } 219 void SetCursorPosition(const Coordinates& aPosition); 220 SetHandleMouseInputs(bool aValue)221 inline void SetHandleMouseInputs (bool aValue){ mHandleMouseInputs = aValue;} IsHandleMouseInputsEnabled()222 inline bool IsHandleMouseInputsEnabled() const { return mHandleKeyboardInputs; } 223 SetHandleKeyboardInputs(bool aValue)224 inline void SetHandleKeyboardInputs (bool aValue){ mHandleKeyboardInputs = aValue;} IsHandleKeyboardInputsEnabled()225 inline bool IsHandleKeyboardInputsEnabled() const { return mHandleKeyboardInputs; } 226 SetImGuiChildIgnored(bool aValue)227 inline void SetImGuiChildIgnored (bool aValue){ mIgnoreImGuiChild = aValue;} IsImGuiChildIgnored()228 inline bool IsImGuiChildIgnored() const { return mIgnoreImGuiChild; } 229 SetShowWhitespaces(bool aValue)230 inline void SetShowWhitespaces(bool aValue) { mShowWhitespaces = aValue; } IsShowingWhitespaces()231 inline bool IsShowingWhitespaces() const { return mShowWhitespaces; } 232 233 void SetTabSize(int aValue); GetTabSize()234 inline int GetTabSize() const { return mTabSize; } 235 236 void InsertText(const std::string& aValue); 237 void InsertText(const char* aValue); 238 239 void MoveUp(int aAmount = 1, bool aSelect = false); 240 void MoveDown(int aAmount = 1, bool aSelect = false); 241 void MoveLeft(int aAmount = 1, bool aSelect = false, bool aWordMode = false); 242 void MoveRight(int aAmount = 1, bool aSelect = false, bool aWordMode = false); 243 void MoveTop(bool aSelect = false); 244 void MoveBottom(bool aSelect = false); 245 void MoveHome(bool aSelect = false); 246 void MoveEnd(bool aSelect = false); 247 248 void SetSelectionStart(const Coordinates& aPosition); 249 void SetSelectionEnd(const Coordinates& aPosition); 250 void SetSelection(const Coordinates& aStart, const Coordinates& aEnd, SelectionMode aMode = SelectionMode::Normal); 251 void SelectWordUnderCursor(); 252 void SelectAll(); 253 bool HasSelection() const; 254 255 void Copy(); 256 void Cut(); 257 void Paste(); 258 void Delete(); 259 260 bool CanUndo() const; 261 bool CanRedo() const; 262 void Undo(int aSteps = 1); 263 void Redo(int aSteps = 1); 264 265 static const Palette& GetDarkPalette(); 266 static const Palette& GetLightPalette(); 267 static const Palette& GetRetroBluePalette(); 268 269 private: 270 typedef std::vector<std::pair<std::regex, PaletteIndex>> RegexList; 271 272 struct EditorState 273 { 274 Coordinates mSelectionStart; 275 Coordinates mSelectionEnd; 276 Coordinates mCursorPosition; 277 }; 278 279 class UndoRecord 280 { 281 public: UndoRecord()282 UndoRecord() {} ~UndoRecord()283 ~UndoRecord() {} 284 285 UndoRecord( 286 const std::string& aAdded, 287 const TextEditor::Coordinates aAddedStart, 288 const TextEditor::Coordinates aAddedEnd, 289 290 const std::string& aRemoved, 291 const TextEditor::Coordinates aRemovedStart, 292 const TextEditor::Coordinates aRemovedEnd, 293 294 TextEditor::EditorState& aBefore, 295 TextEditor::EditorState& aAfter); 296 297 void Undo(TextEditor* aEditor); 298 void Redo(TextEditor* aEditor); 299 300 std::string mAdded; 301 Coordinates mAddedStart; 302 Coordinates mAddedEnd; 303 304 std::string mRemoved; 305 Coordinates mRemovedStart; 306 Coordinates mRemovedEnd; 307 308 EditorState mBefore; 309 EditorState mAfter; 310 }; 311 312 typedef std::vector<UndoRecord> UndoBuffer; 313 314 void ProcessInputs(); 315 void Colorize(int aFromLine = 0, int aCount = -1); 316 void ColorizeRange(int aFromLine = 0, int aToLine = 0); 317 void ColorizeInternal(); 318 float TextDistanceToLineStart(const Coordinates& aFrom) const; 319 void EnsureCursorVisible(); 320 int GetPageSize() const; 321 std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const; 322 Coordinates GetActualCursorCoordinates() const; 323 Coordinates SanitizeCoordinates(const Coordinates& aValue) const; 324 void Advance(Coordinates& aCoordinates) const; 325 void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd); 326 int InsertTextAt(Coordinates& aWhere, const char* aValue); 327 void AddUndo(UndoRecord& aValue); 328 Coordinates ScreenPosToCoordinates(const ImVec2& aPosition) const; 329 Coordinates FindWordStart(const Coordinates& aFrom) const; 330 Coordinates FindWordEnd(const Coordinates& aFrom) const; 331 Coordinates FindNextWord(const Coordinates& aFrom) const; 332 int GetCharacterIndex(const Coordinates& aCoordinates) const; 333 int GetCharacterColumn(int aLine, int aIndex) const; 334 int GetLineCharacterCount(int aLine) const; 335 int GetLineMaxColumn(int aLine) const; 336 bool IsOnWordBoundary(const Coordinates& aAt) const; 337 void RemoveLine(int aStart, int aEnd); 338 void RemoveLine(int aIndex); 339 Line& InsertLine(int aIndex); 340 void EnterCharacter(ImWchar aChar, bool aShift); 341 void Backspace(); 342 void DeleteSelection(); 343 std::string GetWordUnderCursor() const; 344 std::string GetWordAt(const Coordinates& aCoords) const; 345 ImU32 GetGlyphColor(const Glyph& aGlyph) const; 346 347 void HandleKeyboardInputs(); 348 void HandleMouseInputs(); 349 void Render(); 350 351 float mLineSpacing; 352 Lines mLines; 353 EditorState mState; 354 UndoBuffer mUndoBuffer; 355 int mUndoIndex; 356 357 int mTabSize; 358 bool mOverwrite; 359 bool mReadOnly; 360 bool mWithinRender; 361 bool mScrollToCursor; 362 bool mScrollToTop; 363 bool mTextChanged; 364 bool mColorizerEnabled; 365 float mTextStart; // position (in pixels) where a code line starts relative to the left of the TextEditor. 366 int mLeftMargin; 367 bool mCursorPositionChanged; 368 int mColorRangeMin, mColorRangeMax; 369 SelectionMode mSelectionMode; 370 bool mHandleKeyboardInputs; 371 bool mHandleMouseInputs; 372 bool mIgnoreImGuiChild; 373 bool mShowWhitespaces; 374 375 Palette mPaletteBase; 376 Palette mPalette; 377 LanguageDefinition mLanguageDefinition; 378 RegexList mRegexList; 379 380 bool mCheckComments; 381 Breakpoints mBreakpoints; 382 ErrorMarkers mErrorMarkers; 383 ImVec2 mCharAdvance; 384 Coordinates mInteractiveStart, mInteractiveEnd; 385 std::string mLineBuffer; 386 uint64_t mStartTime; 387 388 float mLastClick; 389 }; 390 391 bool TokenizeCStyleString(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end); 392 bool TokenizeCStyleCharacterLiteral(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end); 393 bool TokenizeCStyleIdentifier(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end); 394 bool TokenizeCStyleNumber(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end); 395 bool TokenizeCStylePunctuation(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end);