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);