TextEditor.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. #pragma once
  2. #include <cassert>
  3. #include <iostream>
  4. #include <string>
  5. #include <vector>
  6. #include <array>
  7. #include <memory>
  8. #include <unordered_set>
  9. #include <unordered_map>
  10. #include <map>
  11. #include "boost/regex.hpp"
  12. #include "imgui.h"
  13. class IMGUI_API TextEditor
  14. {
  15. public:
  16. // ------------- Exposed API ------------- //
  17. TextEditor();
  18. ~TextEditor();
  19. enum class PaletteId
  20. {
  21. Dark, Light, Mariana, RetroBlue
  22. };
  23. enum class LanguageDefinitionId
  24. {
  25. None, Cpp, C, Cs, Python, Lua, Json, Sql, AngelScript, Glsl, Hlsl
  26. };
  27. enum class SetViewAtLineMode
  28. {
  29. FirstVisibleLine, Centered, LastVisibleLine
  30. };
  31. inline void SetReadOnlyEnabled(bool aValue) { mReadOnly = aValue; }
  32. inline bool IsReadOnlyEnabled() const { return mReadOnly; }
  33. inline void SetAutoIndentEnabled(bool aValue) { mAutoIndent = aValue; }
  34. inline bool IsAutoIndentEnabled() const { return mAutoIndent; }
  35. inline void SetShowWhitespacesEnabled(bool aValue) { mShowWhitespaces = aValue; }
  36. inline bool IsShowWhitespacesEnabled() const { return mShowWhitespaces; }
  37. inline void SetShowLineNumbersEnabled(bool aValue) { mShowLineNumbers = aValue; }
  38. inline bool IsShowLineNumbersEnabled() const { return mShowLineNumbers; }
  39. inline void SetShortTabsEnabled(bool aValue) { mShortTabs = aValue; }
  40. inline bool IsShortTabsEnabled() const { return mShortTabs; }
  41. inline int GetLineCount() const { return (int)mLines.size(); }
  42. inline bool IsOverwriteEnabled() const { return mOverwrite; }
  43. void SetPalette(PaletteId aValue);
  44. PaletteId GetPalette() const { return mPaletteId; }
  45. void SetLanguageDefinition(LanguageDefinitionId aValue);
  46. LanguageDefinitionId GetLanguageDefinition() const { return mLanguageDefinitionId; };
  47. const char* GetLanguageDefinitionName() const;
  48. void SetTabSize(int aValue);
  49. inline int GetTabSize() const { return mTabSize; }
  50. void SetLineSpacing(float aValue);
  51. inline float GetLineSpacing() const { return mLineSpacing; }
  52. inline bool HasTextChanged() const { return mTextChanged; }
  53. inline void ResetTextChanged() { mTextChanged = false; }
  54. inline static void SetDefaultPalette(PaletteId aValue) { defaultPalette = aValue; }
  55. inline static PaletteId GetDefaultPalette() { return defaultPalette; }
  56. void SelectAll();
  57. void SelectLine(int aLine);
  58. void SelectRegion(int aStartLine, int aStartChar, int aEndLine, int aEndChar);
  59. void SelectNextOccurrenceOf(const char* aText, int aTextSize, bool aCaseSensitive = true);
  60. void SelectAllOccurrencesOf(const char* aText, int aTextSize, bool aCaseSensitive = true);
  61. bool AnyCursorHasSelection() const;
  62. bool AllCursorsHaveSelection() const;
  63. void ClearExtraCursors();
  64. void ClearSelections();
  65. void SetCursorPosition(int aLine, int aCharIndex);
  66. inline void GetCursorPosition(int& outLine, int& outColumn) const
  67. {
  68. auto coords = GetActualCursorCoordinates();
  69. outLine = coords.mLine;
  70. outColumn = coords.mColumn;
  71. }
  72. int GetFirstVisibleLine();
  73. int GetLastVisibleLine();
  74. void SetViewAtLine(int aLine, SetViewAtLineMode aMode);
  75. void Copy();
  76. void Cut();
  77. void Paste();
  78. void Undo(int aSteps = 1);
  79. void Redo(int aSteps = 1);
  80. inline bool CanUndo() const { return !mReadOnly && mUndoIndex > 0; };
  81. inline bool CanRedo() const { return !mReadOnly && mUndoIndex < (int)mUndoBuffer.size(); };
  82. inline int GetUndoIndex() const { return mUndoIndex; };
  83. void SetText(const std::string& aText);
  84. std::string GetText() const;
  85. void SetTextLines(const std::vector<std::string>& aLines);
  86. std::vector<std::string> GetTextLines() const;
  87. bool Render(const char* aTitle, bool aParentIsFocused = false, const ImVec2& aSize = ImVec2(), bool aBorder = false);
  88. void ImGuiDebugPanel(const std::string& panelName = "Debug");
  89. void UnitTests();
  90. private:
  91. // ------------- Generic utils ------------- //
  92. static inline ImVec4 U32ColorToVec4(ImU32 in)
  93. {
  94. float s = 1.0f / 255.0f;
  95. return ImVec4(
  96. ((in >> IM_COL32_A_SHIFT) & 0xFF) * s,
  97. ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
  98. ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
  99. ((in >> IM_COL32_R_SHIFT) & 0xFF) * s);
  100. }
  101. static inline bool IsUTFSequence(char c)
  102. {
  103. return (c & 0xC0) == 0x80;
  104. }
  105. static inline float Distance(const ImVec2& a, const ImVec2& b)
  106. {
  107. float x = a.x - b.x;
  108. float y = a.y - b.y;
  109. return sqrt(x * x + y * y);
  110. }
  111. template<typename T>
  112. static inline T Max(T a, T b) { return a > b ? a : b; }
  113. template<typename T>
  114. static inline T Min(T a, T b) { return a < b ? a : b; }
  115. // ------------- Internal ------------- //
  116. enum class PaletteIndex
  117. {
  118. Default,
  119. Keyword,
  120. Number,
  121. String,
  122. CharLiteral,
  123. Punctuation,
  124. Preprocessor,
  125. Identifier,
  126. KnownIdentifier,
  127. PreprocIdentifier,
  128. Comment,
  129. MultiLineComment,
  130. Background,
  131. Cursor,
  132. Selection,
  133. ErrorMarker,
  134. ControlCharacter,
  135. Breakpoint,
  136. LineNumber,
  137. CurrentLineFill,
  138. CurrentLineFillInactive,
  139. CurrentLineEdge,
  140. Max
  141. };
  142. // Represents a character coordinate from the user's point of view,
  143. // i. e. consider an uniform grid (assuming fixed-width font) on the
  144. // screen as it is rendered, and each cell has its own coordinate, starting from 0.
  145. // Tabs are counted as [1..mTabSize] count empty spaces, depending on
  146. // how many space is necessary to reach the next tab stop.
  147. // For example, coordinate (1, 5) represents the character 'B' in a line "\tABC", when mTabSize = 4,
  148. // because it is rendered as " ABC" on the screen.
  149. struct Coordinates
  150. {
  151. int mLine, mColumn;
  152. Coordinates() : mLine(0), mColumn(0) {}
  153. Coordinates(int aLine, int aColumn) : mLine(aLine), mColumn(aColumn)
  154. {
  155. assert(aLine >= 0);
  156. assert(aColumn >= 0);
  157. }
  158. static Coordinates Invalid() { static Coordinates invalid(-1, -1); return invalid; }
  159. bool operator ==(const Coordinates& o) const
  160. {
  161. return
  162. mLine == o.mLine &&
  163. mColumn == o.mColumn;
  164. }
  165. bool operator !=(const Coordinates& o) const
  166. {
  167. return
  168. mLine != o.mLine ||
  169. mColumn != o.mColumn;
  170. }
  171. bool operator <(const Coordinates& o) const
  172. {
  173. if (mLine != o.mLine)
  174. return mLine < o.mLine;
  175. return mColumn < o.mColumn;
  176. }
  177. bool operator >(const Coordinates& o) const
  178. {
  179. if (mLine != o.mLine)
  180. return mLine > o.mLine;
  181. return mColumn > o.mColumn;
  182. }
  183. bool operator <=(const Coordinates& o) const
  184. {
  185. if (mLine != o.mLine)
  186. return mLine < o.mLine;
  187. return mColumn <= o.mColumn;
  188. }
  189. bool operator >=(const Coordinates& o) const
  190. {
  191. if (mLine != o.mLine)
  192. return mLine > o.mLine;
  193. return mColumn >= o.mColumn;
  194. }
  195. Coordinates operator -(const Coordinates& o)
  196. {
  197. return Coordinates(mLine - o.mLine, mColumn - o.mColumn);
  198. }
  199. Coordinates operator +(const Coordinates& o)
  200. {
  201. return Coordinates(mLine + o.mLine, mColumn + o.mColumn);
  202. }
  203. };
  204. struct Cursor
  205. {
  206. Coordinates mInteractiveStart = { 0, 0 };
  207. Coordinates mInteractiveEnd = { 0, 0 };
  208. inline Coordinates GetSelectionStart() const { return mInteractiveStart < mInteractiveEnd ? mInteractiveStart : mInteractiveEnd; }
  209. inline Coordinates GetSelectionEnd() const { return mInteractiveStart > mInteractiveEnd ? mInteractiveStart : mInteractiveEnd; }
  210. inline bool HasSelection() const { return mInteractiveStart != mInteractiveEnd; }
  211. };
  212. struct EditorState // state to be restored with undo/redo
  213. {
  214. int mCurrentCursor = 0;
  215. int mLastAddedCursor = 0;
  216. std::vector<Cursor> mCursors = { {{0,0}} };
  217. void AddCursor();
  218. int GetLastAddedCursorIndex();
  219. void SortCursorsFromTopToBottom();
  220. };
  221. struct Identifier
  222. {
  223. Coordinates mLocation;
  224. std::string mDeclaration;
  225. };
  226. typedef std::unordered_map<std::string, Identifier> Identifiers;
  227. typedef std::array<ImU32, (unsigned)PaletteIndex::Max> Palette;
  228. struct Glyph
  229. {
  230. char mChar;
  231. PaletteIndex mColorIndex = PaletteIndex::Default;
  232. bool mComment : 1;
  233. bool mMultiLineComment : 1;
  234. bool mPreprocessor : 1;
  235. Glyph(char aChar, PaletteIndex aColorIndex) : mChar(aChar), mColorIndex(aColorIndex),
  236. mComment(false), mMultiLineComment(false), mPreprocessor(false) {}
  237. };
  238. typedef std::vector<Glyph> Line;
  239. struct LanguageDefinition
  240. {
  241. typedef std::pair<std::string, PaletteIndex> TokenRegexString;
  242. typedef bool(*TokenizeCallback)(const char* in_begin, const char* in_end, const char*& out_begin, const char*& out_end, PaletteIndex& paletteIndex);
  243. std::string mName;
  244. std::unordered_set<std::string> mKeywords;
  245. Identifiers mIdentifiers;
  246. Identifiers mPreprocIdentifiers;
  247. std::string mCommentStart, mCommentEnd, mSingleLineComment;
  248. char mPreprocChar = '#';
  249. TokenizeCallback mTokenize = nullptr;
  250. std::vector<TokenRegexString> mTokenRegexStrings;
  251. bool mCaseSensitive = true;
  252. static const LanguageDefinition& Cpp();
  253. static const LanguageDefinition& Hlsl();
  254. static const LanguageDefinition& Glsl();
  255. static const LanguageDefinition& Python();
  256. static const LanguageDefinition& C();
  257. static const LanguageDefinition& Sql();
  258. static const LanguageDefinition& AngelScript();
  259. static const LanguageDefinition& Lua();
  260. static const LanguageDefinition& Cs();
  261. static const LanguageDefinition& Json();
  262. };
  263. enum class UndoOperationType { Add, Delete };
  264. struct UndoOperation
  265. {
  266. std::string mText;
  267. TextEditor::Coordinates mStart;
  268. TextEditor::Coordinates mEnd;
  269. UndoOperationType mType;
  270. };
  271. typedef std::vector<std::pair<boost::regex, PaletteIndex>> RegexList;
  272. class UndoRecord
  273. {
  274. public:
  275. UndoRecord() {}
  276. ~UndoRecord() {}
  277. UndoRecord(
  278. const std::vector<UndoOperation>& aOperations,
  279. TextEditor::EditorState& aBefore,
  280. TextEditor::EditorState& aAfter);
  281. void Undo(TextEditor* aEditor);
  282. void Redo(TextEditor* aEditor);
  283. std::vector<UndoOperation> mOperations;
  284. EditorState mBefore;
  285. EditorState mAfter;
  286. };
  287. std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const;
  288. std::string GetClipboardText() const;
  289. std::string GetSelectedText(int aCursor = -1) const;
  290. void SetCursorPosition(const Coordinates& aPosition, int aCursor = -1, bool aClearSelection = true);
  291. int InsertTextAt(Coordinates& aWhere, const char* aValue);
  292. void InsertTextAtCursor(const std::string& aValue, int aCursor = -1);
  293. void InsertTextAtCursor(const char* aValue, int aCursor = -1);
  294. enum class MoveDirection { Right = 0, Left = 1, Up = 2, Down = 3 };
  295. bool Move(int& aLine, int& aCharIndex, bool aLeft = false, bool aLockLine = false) const;
  296. void MoveCharIndexAndColumn(int aLine, int& aCharIndex, int& aColumn) const;
  297. void MoveCoords(Coordinates& aCoords, MoveDirection aDirection, bool aWordMode = false, int aLineCount = 1) const;
  298. void MoveUp(int aAmount = 1, bool aSelect = false);
  299. void MoveDown(int aAmount = 1, bool aSelect = false);
  300. void MoveLeft(bool aSelect = false, bool aWordMode = false);
  301. void MoveRight(bool aSelect = false, bool aWordMode = false);
  302. void MoveTop(bool aSelect = false);
  303. void MoveBottom(bool aSelect = false);
  304. void MoveHome(bool aSelect = false);
  305. void MoveEnd(bool aSelect = false);
  306. void EnterCharacter(ImWchar aChar, bool aShift);
  307. void Backspace(bool aWordMode = false);
  308. void Delete(bool aWordMode = false, const EditorState* aEditorState = nullptr);
  309. void SetSelection(Coordinates aStart, Coordinates aEnd, int aCursor = -1);
  310. void SetSelection(int aStartLine, int aStartChar, int aEndLine, int aEndChar, int aCursor = -1);
  311. void SelectNextOccurrenceOf(const char* aText, int aTextSize, int aCursor = -1, bool aCaseSensitive = true);
  312. void AddCursorForNextOccurrence(bool aCaseSensitive = true);
  313. bool FindNextOccurrence(const char* aText, int aTextSize, const Coordinates& aFrom, Coordinates& outStart, Coordinates& outEnd, bool aCaseSensitive = true);
  314. bool FindMatchingBracket(int aLine, int aCharIndex, Coordinates& out);
  315. void ChangeCurrentLinesIndentation(bool aIncrease);
  316. void MoveUpCurrentLines();
  317. void MoveDownCurrentLines();
  318. void ToggleLineComment();
  319. void RemoveCurrentLines();
  320. float TextDistanceToLineStart(const Coordinates& aFrom, bool aSanitizeCoords = true) const;
  321. void EnsureCursorVisible(int aCursor = -1, bool aStartToo = false);
  322. Coordinates SanitizeCoordinates(const Coordinates& aValue) const;
  323. Coordinates GetActualCursorCoordinates(int aCursor = -1, bool aStart = false) const;
  324. Coordinates ScreenPosToCoordinates(const ImVec2& aPosition, bool aInsertionMode = false, bool* isOverLineNumber = nullptr) const;
  325. Coordinates FindWordStart(const Coordinates& aFrom) const;
  326. Coordinates FindWordEnd(const Coordinates& aFrom) const;
  327. int GetCharacterIndexL(const Coordinates& aCoordinates) const;
  328. int GetCharacterIndexR(const Coordinates& aCoordinates) const;
  329. int GetCharacterColumn(int aLine, int aIndex) const;
  330. int GetFirstVisibleCharacterIndex(int aLine) const;
  331. int GetLineMaxColumn(int aLine, int aLimit = -1) const;
  332. Line& InsertLine(int aIndex);
  333. void RemoveLine(int aIndex, const std::unordered_set<int>* aHandledCursors = nullptr);
  334. void RemoveLines(int aStart, int aEnd);
  335. void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd);
  336. void DeleteSelection(int aCursor = -1);
  337. void RemoveGlyphsFromLine(int aLine, int aStartChar, int aEndChar = -1);
  338. void AddGlyphsToLine(int aLine, int aTargetIndex, Line::iterator aSourceStart, Line::iterator aSourceEnd);
  339. void AddGlyphToLine(int aLine, int aTargetIndex, Glyph aGlyph);
  340. ImU32 GetGlyphColor(const Glyph& aGlyph) const;
  341. void HandleKeyboardInputs(bool aParentIsFocused = false);
  342. void HandleMouseInputs();
  343. void UpdateViewVariables(float aScrollX, float aScrollY);
  344. void Render(bool aParentIsFocused = false);
  345. void OnCursorPositionChanged();
  346. void OnLineChanged(bool aBeforeChange, int aLine, int aColumn, int aCharCount, bool aDeleted);
  347. void MergeCursorsIfPossible();
  348. void AddUndo(UndoRecord& aValue);
  349. void Colorize(int aFromLine = 0, int aCount = -1);
  350. void ColorizeRange(int aFromLine = 0, int aToLine = 0);
  351. void ColorizeInternal();
  352. std::vector<Line> mLines;
  353. EditorState mState;
  354. std::vector<UndoRecord> mUndoBuffer;
  355. int mUndoIndex = 0;
  356. int mTabSize = 4;
  357. float mLineSpacing = 1.0f;
  358. bool mOverwrite = false;
  359. bool mReadOnly = false;
  360. bool mAutoIndent = true;
  361. bool mShowWhitespaces = true;
  362. bool mShowLineNumbers = true;
  363. bool mShortTabs = false;
  364. bool mTextChanged = false;
  365. int mSetViewAtLine = -1;
  366. SetViewAtLineMode mSetViewAtLineMode;
  367. int mEnsureCursorVisible = -1;
  368. bool mEnsureCursorVisibleStartToo = false;
  369. bool mScrollToTop = false;
  370. float mTextStart = 20.0f; // position (in pixels) where a code line starts relative to the left of the TextEditor.
  371. int mLeftMargin = 10;
  372. ImVec2 mCharAdvance;
  373. float mCurrentSpaceHeight = 20.0f;
  374. float mCurrentSpaceWidth = 20.0f;
  375. float mLastClickTime = -1.0f;
  376. ImVec2 mLastClickPos;
  377. int mFirstVisibleLine = 0;
  378. int mLastVisibleLine = 0;
  379. int mVisibleLineCount = 0;
  380. int mFirstVisibleColumn = 0;
  381. int mLastVisibleColumn = 0;
  382. int mVisibleColumnCount = 0;
  383. float mContentWidth = 0.0f;
  384. float mContentHeight = 0.0f;
  385. float mScrollX = 0.0f;
  386. float mScrollY = 0.0f;
  387. bool mPanning = false;
  388. bool mDraggingSelection = false;
  389. ImVec2 mLastMousePos;
  390. bool mCursorPositionChanged = false;
  391. bool mCursorOnBracket = false;
  392. Coordinates mMatchingBracketCoords;
  393. int mColorRangeMin = 0;
  394. int mColorRangeMax = 0;
  395. bool mCheckComments = true;
  396. PaletteId mPaletteId;
  397. Palette mPalette;
  398. LanguageDefinitionId mLanguageDefinitionId;
  399. const LanguageDefinition* mLanguageDefinition = nullptr;
  400. RegexList mRegexList;
  401. std::string mLineBuffer;
  402. inline bool IsHorizontalScrollbarVisible() const { return mCurrentSpaceWidth > mContentWidth; }
  403. inline bool IsVerticalScrollbarVisible() const { return mCurrentSpaceHeight > mContentHeight; }
  404. inline int TabSizeAtColumn(int aColumn) const { return mTabSize - (aColumn % mTabSize); }
  405. static const Palette& GetDarkPalette();
  406. static const Palette& GetMarianaPalette();
  407. static const Palette& GetLightPalette();
  408. static const Palette& GetRetroBluePalette();
  409. static const std::unordered_map<char, char> OPEN_TO_CLOSE_CHAR;
  410. static const std::unordered_map<char, char> CLOSE_TO_OPEN_CHAR;
  411. static PaletteId defaultPalette;
  412. };