Merge branch 'master' into dev
diff --git a/TextEditor.cpp b/TextEditor.cpp
index 53e5553..8b30246 100644
--- a/TextEditor.cpp
+++ b/TextEditor.cpp
@@ -1,3159 +1,3158 @@
-#include <algorithm>

-#include <chrono>

-#include <string>

-#include <regex>

-#include <cmath>

-

-#include "TextEditor.h"

-

-#define IMGUI_DEFINE_MATH_OPERATORS

-#include "imgui.h" // for imGui::GetCurrentWindow()

-

-// TODO

-// - multiline comments vs single-line: latter is blocking start of a ML

-

-template<class InputIt1, class InputIt2, class BinaryPredicate>

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

-}

-

-TextEditor::TextEditor()

-	: mLineSpacing(1.0f)

-	, mUndoIndex(0)

-	, mTabSize(4)

-	, mOverwrite(false)

-	, mReadOnly(false)

-	, mWithinRender(false)

-	, mScrollToCursor(false)

-	, mScrollToTop(false)

-	, mTextChanged(false)

-	, mColorizerEnabled(true)

-	, mTextStart(20.0f)

-	, mLeftMargin(10)

-	, mCursorPositionChanged(false)

-	, mColorRangeMin(0)

-	, mColorRangeMax(0)

-	, mSelectionMode(SelectionMode::Normal)

-	, mCheckComments(true)

-	, mLastClick(-1.0f)

-	, mHandleKeyboardInputs(true)

-	, mHandleMouseInputs(true)

-	, mIgnoreImGuiChild(false)

-	, mShowWhitespaces(true)

-	, mStartTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())

-{

-	SetPalette(GetDarkPalette());

-	SetLanguageDefinition(LanguageDefinition::HLSL());

-	mLines.push_back(Line());

-}

-

-TextEditor::~TextEditor()

-{

-}

-

-void TextEditor::SetLanguageDefinition(const LanguageDefinition & aLanguageDef)

-{

-	mLanguageDefinition = aLanguageDef;

-	mRegexList.clear();

-

-	for (auto& r : mLanguageDefinition.mTokenRegexStrings)

-		mRegexList.push_back(std::make_pair(std::regex(r.first, std::regex_constants::optimize), r.second));

-

-	Colorize();

-}

-

-void TextEditor::SetPalette(const Palette & aValue)

-{

-	mPaletteBase = aValue;

-}

-

-std::string TextEditor::GetText(const Coordinates & aStart, const Coordinates & aEnd) const

-{

-	std::string result;

-

-	auto lstart = aStart.mLine;

-	auto lend = aEnd.mLine;

-	auto istart = GetCharacterIndex(aStart);

-	auto iend = GetCharacterIndex(aEnd);

-	size_t s = 0;

-

-	for (size_t i = lstart; i < lend; i++)

-		s += mLines[i].size();

-

-	result.reserve(s + s / 8);

-

-	while (istart < iend || lstart < lend)

-	{

-		if (lstart >= (int)mLines.size())

-			break;

-

-		auto& line = mLines[lstart];

-		if (istart < (int)line.size())

-		{

-			result += line[istart].mChar;

-			istart++;

-		}

-		else

-		{

-			istart = 0;

-			++lstart;

-			result += '\n';

-		}

-	}

-

-	return result;

-}

-

-TextEditor::Coordinates TextEditor::GetActualCursorCoordinates() const

-{

-	return SanitizeCoordinates(mState.mCursorPosition);

-}

-

-TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates & aValue) const

-{

-	auto line = aValue.mLine;

-	auto column = aValue.mColumn;

-	if (line >= (int)mLines.size())

-	{

-		if (mLines.empty())

-		{

-			line = 0;

-			column = 0;

-		}

-		else

-		{

-			line = (int)mLines.size() - 1;

-			column = GetLineMaxColumn(line);

-		}

-		return Coordinates(line, column);

-	}

-	else

-	{

-		column = mLines.empty() ? 0 : std::min(column, GetLineMaxColumn(line));

-		return Coordinates(line, column);

-	}

-}

-

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

-static int UTF8CharLength(TextEditor::Char c)

-{

-	if ((c & 0xFE) == 0xFC)

-		return 6;

-	if ((c & 0xFC) == 0xF8)

-		return 5;

-	if ((c & 0xF8) == 0xF0)

-		return 4;

-	else if ((c & 0xF0) == 0xE0)

-		return 3;

-	else if ((c & 0xE0) == 0xC0)

-		return 2;

-	return 1;

-}

-

-// "Borrowed" from ImGui source

-static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)

-{

-	if (c < 0x80)

-	{

-		buf[0] = (char)c;

-		return 1;

-	}

-	if (c < 0x800)

-	{

-		if (buf_size < 2) return 0;

-		buf[0] = (char)(0xc0 + (c >> 6));

-		buf[1] = (char)(0x80 + (c & 0x3f));

-		return 2;

-	}

-	if (c >= 0xdc00 && c < 0xe000)

-	{

-		return 0;

-	}

-	if (c >= 0xd800 && c < 0xdc00)

-	{

-		if (buf_size < 4) return 0;

-		buf[0] = (char)(0xf0 + (c >> 18));

-		buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));

-		buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));

-		buf[3] = (char)(0x80 + ((c) & 0x3f));

-		return 4;

-	}

-	//else if (c < 0x10000)

-	{

-		if (buf_size < 3) return 0;

-		buf[0] = (char)(0xe0 + (c >> 12));

-		buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));

-		buf[2] = (char)(0x80 + ((c) & 0x3f));

-		return 3;

-	}

-}

-

-void TextEditor::Advance(Coordinates & aCoordinates) const

-{

-	if (aCoordinates.mLine < (int)mLines.size())

-	{

-		auto& line = mLines[aCoordinates.mLine];

-		auto cindex = GetCharacterIndex(aCoordinates);

-

-		if (cindex + 1 < (int)line.size())

-		{

-			auto delta = UTF8CharLength(line[cindex].mChar);

-			cindex = std::min(cindex + delta, (int)line.size() - 1);

-		}

-		else

-		{

-			++aCoordinates.mLine;

-			cindex = 0;

-		}

-		aCoordinates.mColumn = GetCharacterColumn(aCoordinates.mLine, cindex);

-	}

-}

-

-void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEnd)

-{

-	assert(aEnd >= aStart);

-	assert(!mReadOnly);

-

-	//printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine, aEnd.mColumn);

-

-	if (aEnd == aStart)

-		return;

-

-	auto start = GetCharacterIndex(aStart);

-	auto end = GetCharacterIndex(aEnd);

-

-	if (aStart.mLine == aEnd.mLine)

-	{

-		auto& line = mLines[aStart.mLine];

-		auto n = GetLineMaxColumn(aStart.mLine);

-		if (aEnd.mColumn >= n)

-			line.erase(line.begin() + start, line.end());

-		else

-			line.erase(line.begin() + start, line.begin() + end);

-	}

-	else

-	{

-		auto& firstLine = mLines[aStart.mLine];

-		auto& lastLine = mLines[aEnd.mLine];

-

-		firstLine.erase(firstLine.begin() + start, firstLine.end());

-		lastLine.erase(lastLine.begin(), lastLine.begin() + end);

-

-		if (aStart.mLine < aEnd.mLine)

-			firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end());

-

-		if (aStart.mLine < aEnd.mLine)

-			RemoveLine(aStart.mLine + 1, aEnd.mLine + 1);

-	}

-

-	mTextChanged = true;

-}

-

-int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValue)

-{

-	assert(!mReadOnly);

-

-	int cindex = GetCharacterIndex(aWhere);

-	int totalLines = 0;

-	while (*aValue != '\0')

-	{

-		assert(!mLines.empty());

-

-		if (*aValue == '\r')

-		{

-			// skip

-			++aValue;

-		}

-		else if (*aValue == '\n')

-		{

-			if (cindex < (int)mLines[aWhere.mLine].size())

-			{

-				auto& newLine = InsertLine(aWhere.mLine + 1);

-				auto& line = mLines[aWhere.mLine];

-				newLine.insert(newLine.begin(), line.begin() + cindex, line.end());

-				line.erase(line.begin() + cindex, line.end());

-			}

-			else

-			{

-				InsertLine(aWhere.mLine + 1);

-			}

-			++aWhere.mLine;

-			aWhere.mColumn = 0;

-			cindex = 0;

-			++totalLines;

-			++aValue;

-		}

-		else

-		{

-			auto& line = mLines[aWhere.mLine];

-			auto d = UTF8CharLength(*aValue);

-			while (d-- > 0 && *aValue != '\0')

-				line.insert(line.begin() + cindex++, Glyph(*aValue++, PaletteIndex::Default));

-			++aWhere.mColumn;

-		}

-

-		mTextChanged = true;

-	}

-

-	return totalLines;

-}

-

-void TextEditor::AddUndo(UndoRecord& aValue)

-{

-	assert(!mReadOnly);

-	//printf("AddUndo: (@%d.%d) +\'%s' [%d.%d .. %d.%d], -\'%s', [%d.%d .. %d.%d] (@%d.%d)\n",

-	//	aValue.mBefore.mCursorPosition.mLine, aValue.mBefore.mCursorPosition.mColumn,

-	//	aValue.mAdded.c_str(), aValue.mAddedStart.mLine, aValue.mAddedStart.mColumn, aValue.mAddedEnd.mLine, aValue.mAddedEnd.mColumn,

-	//	aValue.mRemoved.c_str(), aValue.mRemovedStart.mLine, aValue.mRemovedStart.mColumn, aValue.mRemovedEnd.mLine, aValue.mRemovedEnd.mColumn,

-	//	aValue.mAfter.mCursorPosition.mLine, aValue.mAfter.mCursorPosition.mColumn

-	//	);

-

-	mUndoBuffer.resize((size_t)(mUndoIndex + 1));

-	mUndoBuffer.back() = aValue;

-	++mUndoIndex;

-}

-

-TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPosition) const

-{

-	ImVec2 origin = ImGui::GetCursorScreenPos();

-	ImVec2 local(aPosition.x - origin.x, aPosition.y - origin.y);

-

-	int lineNo = std::max(0, (int)floor(local.y / mCharAdvance.y));

-

-	int columnCoord = 0;

-

-	if (lineNo >= 0 && lineNo < (int)mLines.size())

-	{

-		auto& line = mLines.at(lineNo);

-

-		int columnIndex = 0;

-		std::string cumulatedString = "";

-		float columnWidth = 0.0f;

-		float columnX = 0.0f;

-

-		// First we find the hovered column coord.

-		while (mTextStart + columnX < local.x && (size_t)columnIndex < line.size())

-		{

-			if (line[columnIndex].mChar == '\t')

-			{

-				float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ").x;

-				float oldX = columnX;

-				columnX = (1.0f + std::floor((1.0f + columnX) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);

-				columnWidth = columnX - oldX;

-				columnCoord++;

-			}

-			else

-			{

-				char buf[7];

-				auto d = UTF8CharLength(line[columnIndex].mChar);

-				int i = 0;

-				while (i < 6 && d-- > 0)

-					buf[i++] = line[columnIndex++].mChar;

-				buf[i] = '\0';

-				columnWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x;

-				columnX += columnWidth;

-				columnCoord++;

-			}

-		}

-

-		// Then we reduce by 1 column coord if cursor is on the left side of the hovered column.

-		if (mTextStart + columnX - columnWidth / 2.0f > local.x)

-			columnIndex = std::max(0, columnIndex - 1);

-	}

-

-	return SanitizeCoordinates(Coordinates(lineNo, columnCoord));

-}

-

-TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates & aFrom) const

-{

-	Coordinates at = aFrom;

-	if (at.mLine >= (int)mLines.size())

-		return at;

-

-	auto& line = mLines[at.mLine];

-	auto cindex = GetCharacterIndex(at);

-

-	if (cindex >= (int)line.size())

-		return at;

-

-	while (cindex > 0 && isspace(line[cindex].mChar))

-		--cindex;

-

-	auto cstart = (PaletteIndex)line[cindex].mColorIndex;

-	while (cindex > 0)

-	{

-		auto c = line[cindex].mChar;

-		if ((c & 0xC0) != 0x80)	// not UTF code sequence 10xxxxxx

-		{

-			if (c <= 32 && isspace(c))

-			{

-				cindex++;

-				break;

-			}

-			if (cstart != (PaletteIndex)line[size_t(cindex - 1)].mColorIndex)

-				break;

-		}

-		--cindex;

-	}

-	return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex));

-}

-

-TextEditor::Coordinates TextEditor::FindWordEnd(const Coordinates & aFrom) const

-{

-	Coordinates at = aFrom;

-	if (at.mLine >= (int)mLines.size())

-		return at;

-

-	auto& line = mLines[at.mLine];

-	auto cindex = GetCharacterIndex(at);

-

-	if (cindex >= (int)line.size())

-		return at;

-

-	bool prevspace = (bool)isspace(line[cindex].mChar);

-	auto cstart = (PaletteIndex)line[cindex].mColorIndex;

-	while (cindex < (int)line.size())

-	{

-		auto c = line[cindex].mChar;

-		auto d = UTF8CharLength(c);

-		if (cstart != (PaletteIndex)line[cindex].mColorIndex)

-			break;

-

-		if (prevspace != !!isspace(c))

-		{

-			if (isspace(c))

-				while (cindex < (int)line.size() && isspace(line[cindex].mChar))

-					++cindex;

-			break;

-		}

-		cindex += d;

-	}

-	return Coordinates(aFrom.mLine, GetCharacterColumn(aFrom.mLine, cindex));

-}

-

-TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates & aFrom) const

-{

-	Coordinates at = aFrom;

-	if (at.mLine >= (int)mLines.size())

-		return at;

-

-	// skip to the next non-word character

-	auto cindex = GetCharacterIndex(aFrom);

-	bool isword = false;

-	bool skip = false;

-	if (cindex < (int)mLines[at.mLine].size())

-	{

-		auto& line = mLines[at.mLine];

-		isword = isalnum(line[cindex].mChar);

-		skip = isword;

-	}

-

-	while (!isword || skip)

-	{

-		if (at.mLine >= mLines.size())

-		{

-			auto l = std::max(0, (int) mLines.size() - 1);

-			return Coordinates(l, GetLineMaxColumn(l));

-		}

-

-		auto& line = mLines[at.mLine];

-		if (cindex < (int)line.size())

-		{

-			isword = isalnum(line[cindex].mChar);

-

-			if (isword && !skip)

-				return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex));

-

-			if (!isword)

-				skip = false;

-

-			cindex++;

-		}

-		else

-		{

-			cindex = 0;

-			++at.mLine;

-			skip = false;

-			isword = false;

-		}

-	}

-

-	return at;

-}

-

-int TextEditor::GetCharacterIndex(const Coordinates& aCoordinates) const

-{

-	if (aCoordinates.mLine >= mLines.size())

-		return -1;

-	auto& line = mLines[aCoordinates.mLine];

-	int c = 0;

-	int i = 0;

-	for (; i < line.size() && c < aCoordinates.mColumn;)

-	{

-		if (line[i].mChar == '\t')

-			c = (c / mTabSize) * mTabSize + mTabSize;

-		else

-			++c;

-		i += UTF8CharLength(line[i].mChar);

-	}

-	return i;

-}

-

-int TextEditor::GetCharacterColumn(int aLine, int aIndex) const

-{

-	if (aLine >= mLines.size())

-		return 0;

-	auto& line = mLines[aLine];

-	int col = 0;

-	int i = 0;

-	while (i < aIndex && i < (int)line.size())

-	{

-		auto c = line[i].mChar;

-		i += UTF8CharLength(c);

-		if (c == '\t')

-			col = (col / mTabSize) * mTabSize + mTabSize;

-		else

-			col++;

-	}

-	return col;

-}

-

-int TextEditor::GetLineCharacterCount(int aLine) const

-{

-	if (aLine >= mLines.size())

-		return 0;

-	auto& line = mLines[aLine];

-	int c = 0;

-	for (unsigned i = 0; i < line.size(); c++)

-		i += UTF8CharLength(line[i].mChar);

-	return c;

-}

-

-int TextEditor::GetLineMaxColumn(int aLine) const

-{

-	if (aLine >= mLines.size())

-		return 0;

-	auto& line = mLines[aLine];

-	int col = 0;

-	for (unsigned i = 0; i < line.size(); )

-	{

-		auto c = line[i].mChar;

-		if (c == '\t')

-			col = (col / mTabSize) * mTabSize + mTabSize;

-		else

-			col++;

-		i += UTF8CharLength(c);

-	}

-	return col;

-}

-

-bool TextEditor::IsOnWordBoundary(const Coordinates & aAt) const

-{

-	if (aAt.mLine >= (int)mLines.size() || aAt.mColumn == 0)

-		return true;

-

-	auto& line = mLines[aAt.mLine];

-	auto cindex = GetCharacterIndex(aAt);

-	if (cindex >= (int)line.size())

-		return true;

-

-	if (mColorizerEnabled)

-		return line[cindex].mColorIndex != line[size_t(cindex - 1)].mColorIndex;

-

-	return isspace(line[cindex].mChar) != isspace(line[cindex - 1].mChar);

-}

-

-void TextEditor::RemoveLine(int aStart, int aEnd)

-{

-	assert(!mReadOnly);

-	assert(aEnd >= aStart);

-	assert(mLines.size() > (size_t)(aEnd - aStart));

-

-	ErrorMarkers etmp;

-	for (auto& i : mErrorMarkers)

-	{

-		ErrorMarkers::value_type e(i.first >= aStart ? i.first - 1 : i.first, i.second);

-		if (e.first >= aStart && e.first <= aEnd)

-			continue;

-		etmp.insert(e);

-	}

-	mErrorMarkers = std::move(etmp);

-

-	Breakpoints btmp;

-	for (auto i : mBreakpoints)

-	{

-		if (i >= aStart && i <= aEnd)

-			continue;

-		btmp.insert(i >= aStart ? i - 1 : i);

-	}

-	mBreakpoints = std::move(btmp);

-

-	mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd);

-	assert(!mLines.empty());

-

-	mTextChanged = true;

-}

-

-void TextEditor::RemoveLine(int aIndex)

-{

-	assert(!mReadOnly);

-	assert(mLines.size() > 1);

-

-	ErrorMarkers etmp;

-	for (auto& i : mErrorMarkers)

-	{

-		ErrorMarkers::value_type e(i.first > aIndex ? i.first - 1 : i.first, i.second);

-		if (e.first - 1 == aIndex)

-			continue;

-		etmp.insert(e);

-	}

-	mErrorMarkers = std::move(etmp);

-

-	Breakpoints btmp;

-	for (auto i : mBreakpoints)

-	{

-		if (i == aIndex)

-			continue;

-		btmp.insert(i >= aIndex ? i - 1 : i);

-	}

-	mBreakpoints = std::move(btmp);

-

-	mLines.erase(mLines.begin() + aIndex);

-	assert(!mLines.empty());

-

-	mTextChanged = true;

-}

-

-TextEditor::Line& TextEditor::InsertLine(int aIndex)

-{

-	assert(!mReadOnly);

-

-	auto& result = *mLines.insert(mLines.begin() + aIndex, Line());

-

-	ErrorMarkers etmp;

-	for (auto& i : mErrorMarkers)

-		etmp.insert(ErrorMarkers::value_type(i.first >= aIndex ? i.first + 1 : i.first, i.second));

-	mErrorMarkers = std::move(etmp);

-

-	Breakpoints btmp;

-	for (auto i : mBreakpoints)

-		btmp.insert(i >= aIndex ? i + 1 : i);

-	mBreakpoints = std::move(btmp);

-

-	return result;

-}

-

-std::string TextEditor::GetWordUnderCursor() const

-{

-	auto c = GetCursorPosition();

-	return GetWordAt(c);

-}

-

-std::string TextEditor::GetWordAt(const Coordinates & aCoords) const

-{

-	auto start = FindWordStart(aCoords);

-	auto end = FindWordEnd(aCoords);

-

-	std::string r;

-

-	auto istart = GetCharacterIndex(start);

-	auto iend = GetCharacterIndex(end);

-

-	for (auto it = istart; it < iend; ++it)

-		r.push_back(mLines[aCoords.mLine][it].mChar);

-

-	return r;

-}

-

-ImU32 TextEditor::GetGlyphColor(const Glyph & aGlyph) const

-{

-	if (!mColorizerEnabled)

-		return mPalette[(int)PaletteIndex::Default];

-	if (aGlyph.mComment)

-		return mPalette[(int)PaletteIndex::Comment];

-	if (aGlyph.mMultiLineComment)

-		return mPalette[(int)PaletteIndex::MultiLineComment];

-	auto const color = mPalette[(int)aGlyph.mColorIndex];

-	if (aGlyph.mPreprocessor)

-	{

-		const auto ppcolor = mPalette[(int)PaletteIndex::Preprocessor];

-		const int c0 = ((ppcolor & 0xff) + (color & 0xff)) / 2;

-		const int c1 = (((ppcolor >> 8) & 0xff) + ((color >> 8) & 0xff)) / 2;

-		const int c2 = (((ppcolor >> 16) & 0xff) + ((color >> 16) & 0xff)) / 2;

-		const int c3 = (((ppcolor >> 24) & 0xff) + ((color >> 24) & 0xff)) / 2;

-		return ImU32(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24));

-	}

-	return color;

-}

-

-void TextEditor::HandleKeyboardInputs()

-{

-	ImGuiIO& io = ImGui::GetIO();

-	auto shift = io.KeyShift;

-	auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;

-	auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;

-

-	if (ImGui::IsWindowFocused())

-	{

-		if (ImGui::IsWindowHovered())

-			ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput);

-		//ImGui::CaptureKeyboardFromApp(true);

-

-		io.WantCaptureKeyboard = true;

-		io.WantTextInput = true;

-

-		if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Z)))

-			Undo();

-		else if (!IsReadOnly() && !ctrl && !shift && alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace)))

-			Undo();

-		else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y)))

-			Redo();

-		else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))

-			MoveUp(1, shift);

-		else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))

-			MoveDown(1, shift);

-		else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)))

-			MoveLeft(1, shift, ctrl);

-		else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)))

-			MoveRight(1, shift, ctrl);

-		else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp)))

-			MoveUp(GetPageSize() - 4, shift);

-		else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown)))

-			MoveDown(GetPageSize() - 4, shift);

-		else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))

-			MoveTop(shift);

-		else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))

-			MoveBottom(shift);

-		else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))

-			MoveHome(shift);

-		else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))

-			MoveEnd(shift);

-		else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))

-			Delete();

-		else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace)))

-			Backspace();

-		else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))

-			mOverwrite ^= true;

-		else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))

-			Copy();

-		else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))

-			Copy();

-		else if (!IsReadOnly() && !ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))

-			Paste();

-		else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_V)))

-			Paste();

-		else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_X)))

-			Cut();

-		else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))

-			Cut();

-		else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_A)))

-			SelectAll();

-		else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter)))

-			EnterCharacter('\n', false);

-		else if (!IsReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Tab)))

-			EnterCharacter('\t', shift);

-

-		if (!IsReadOnly() && !io.InputQueueCharacters.empty())

-		{

-			for (int 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.KeySuper : io.KeyCtrl;

-	auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;

-

-	if (ImGui::IsWindowHovered())

-	{

-		if (!shift && !alt)

-		{

-			auto click = ImGui::IsMouseClicked(0);

-			auto doubleClick = ImGui::IsMouseDoubleClicked(0);

-			auto t = ImGui::GetTime();

-			auto tripleClick = click && !doubleClick && (mLastClick != -1.0f && (t - mLastClick) < io.MouseDoubleClickTime);

-

-			/*

-			Left mouse button triple click

-			*/

-

-			if (tripleClick)

-			{

-				if (!ctrl)

-				{

-					mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos()));

-					mSelectionMode = SelectionMode::Line;

-					SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);

-				}

-

-				mLastClick = -1.0f;

-			}

-

-			/*

-			Left mouse button double click

-			*/

-

-			else if (doubleClick)

-			{

-				if (!ctrl)

-				{

-					mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos()));

-					if (mSelectionMode == SelectionMode::Line)

-						mSelectionMode = SelectionMode::Normal;

-					else

-						mSelectionMode = SelectionMode::Word;

-					SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);

-				}

-

-				mLastClick = (float)ImGui::GetTime();

-			}

-

-			/*

-			Left mouse button click

-			*/

-			else if (click)

-			{

-				mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos()));

-				if (ctrl)

-					mSelectionMode = SelectionMode::Word;

-				else

-					mSelectionMode = SelectionMode::Normal;

-				SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);

-

-				mLastClick = (float)ImGui::GetTime();

-			}

-			// Mouse left button dragging (=> update selection)

-			else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0))

-			{

-				io.WantCaptureMouse = true;

-				mState.mCursorPosition = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos()));

-				SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);

-			}

-		}

-	}

-}

-

-void TextEditor::Render()

-{

-	/* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/

-	const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x;

-	mCharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing);

-

-	/* Update palette with the current alpha from style */

-	for (int i = 0; i < (int)PaletteIndex::Max; ++i)

-	{

-		auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]);

-		color.w *= ImGui::GetStyle().Alpha;

-		mPalette[i] = ImGui::ColorConvertFloat4ToU32(color);

-	}

-

-	assert(mLineBuffer.empty());

-

-	auto contentSize = ImGui::GetWindowContentRegionMax();

-	auto drawList = ImGui::GetWindowDrawList();

-	float longest(mTextStart);

-

-	if (mScrollToTop)

-	{

-		mScrollToTop = false;

-		ImGui::SetScrollY(0.f);

-	}

-

-	ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos();

-	auto scrollX = ImGui::GetScrollX();

-	auto scrollY = ImGui::GetScrollY();

-

-	auto lineNo = (int)floor(scrollY / mCharAdvance.y);

-	auto globalLineMax = (int)mLines.size();

-	auto lineMax = std::max(0, std::min((int)mLines.size() - 1, lineNo + (int)floor((scrollY + contentSize.y) / mCharAdvance.y)));

-

-	// Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width

-	char buf[16];

-	snprintf(buf, 16, " %d ", globalLineMax);

-	mTextStart = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x + mLeftMargin;

-

-	if (!mLines.empty())

-	{

-		float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x;

-

-		while (lineNo <= lineMax)

-		{

-			ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * mCharAdvance.y);

-			ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + mTextStart, lineStartScreenPos.y);

-

-			auto& line = mLines[lineNo];

-			longest = std::max(mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest);

-			auto columnNo = 0;

-			Coordinates lineStartCoord(lineNo, 0);

-			Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo));

-

-			// Draw selection for the current line

-			float sstart = -1.0f;

-			float ssend = -1.0f;

-

-			assert(mState.mSelectionStart <= mState.mSelectionEnd);

-			if (mState.mSelectionStart <= lineEndCoord)

-				sstart = mState.mSelectionStart > lineStartCoord ? TextDistanceToLineStart(mState.mSelectionStart) : 0.0f;

-			if (mState.mSelectionEnd > lineStartCoord)

-				ssend = TextDistanceToLineStart(mState.mSelectionEnd < lineEndCoord ? mState.mSelectionEnd : lineEndCoord);

-

-			if (mState.mSelectionEnd.mLine > lineNo)

-				ssend += mCharAdvance.x;

-

-			if (sstart != -1 && ssend != -1 && sstart < ssend)

-			{

-				ImVec2 vstart(lineStartScreenPos.x + mTextStart + sstart, lineStartScreenPos.y);

-				ImVec2 vend(lineStartScreenPos.x + mTextStart + ssend, lineStartScreenPos.y + mCharAdvance.y);

-				drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]);

-			}

-

-			// Draw breakpoints

-			auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y);

-

-			if (mBreakpoints.count(lineNo + 1) != 0)

-			{

-				auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y);

-				drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]);

-			}

-

-			// Draw error markers

-			auto errorIt = mErrorMarkers.find(lineNo + 1);

-			if (errorIt != mErrorMarkers.end())

-			{

-				auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y);

-				drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::ErrorMarker]);

-

-				if (ImGui::IsMouseHoveringRect(lineStartScreenPos, end))

-				{

-					ImGui::BeginTooltip();

-					ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f));

-					ImGui::Text("Error at line %d:", errorIt->first);

-					ImGui::PopStyleColor();

-					ImGui::Separator();

-					ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.2f, 1.0f));

-					ImGui::Text("%s", errorIt->second.c_str());

-					ImGui::PopStyleColor();

-					ImGui::EndTooltip();

-				}

-			}

-

-			// Draw line number (right aligned)

-			snprintf(buf, 16, "%d  ", lineNo + 1);

-

-			auto lineNoWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x;

-			drawList->AddText(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y), mPalette[(int)PaletteIndex::LineNumber], buf);

-

-			if (mState.mCursorPosition.mLine == lineNo)

-			{

-				auto focused = ImGui::IsWindowFocused();

-

-				// Highlight the current line (where the cursor is)

-				if (!HasSelection())

-				{

-					auto end = ImVec2(start.x + contentSize.x + scrollX, start.y + mCharAdvance.y);

-					drawList->AddRectFilled(start, end, mPalette[(int)(focused ? PaletteIndex::CurrentLineFill : PaletteIndex::CurrentLineFillInactive)]);

-					drawList->AddRect(start, end, mPalette[(int)PaletteIndex::CurrentLineEdge], 1.0f);

-				}

-

-				// Render the cursor

-				if (focused)

-				{

-					auto timeEnd = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

-					auto elapsed = timeEnd - mStartTime;

-					if (elapsed > 400)

-					{

-						float width = 1.0f;

-						auto cindex = GetCharacterIndex(mState.mCursorPosition);

-						float cx = TextDistanceToLineStart(mState.mCursorPosition);

-

-						if (mOverwrite && cindex < (int)line.size())

-						{

-							auto c = line[cindex].mChar;

-							if (c == '\t')

-							{

-								auto x = (1.0f + std::floor((1.0f + cx) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);

-								width = x - cx;

-							}

-							else

-							{

-								char buf[2];

-								buf[0] = line[cindex].mChar;

-								buf[1] = '\0';

-								width = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x;

-							}

-						}

-						ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y);

-						ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + mCharAdvance.y);

-						drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]);

-						if (elapsed > 800)

-							mStartTime = timeEnd;

-					}

-				}

-			}

-

-			// Render colorized text

-			auto prevColor = line.empty() ? mPalette[(int)PaletteIndex::Default] : GetGlyphColor(line[0]);

-			ImVec2 bufferOffset;

-

-			for (int i = 0; i < line.size();)

-			{

-				auto& glyph = line[i];

-				auto color = GetGlyphColor(glyph);

-

-				if ((color != prevColor || glyph.mChar == '\t' || glyph.mChar == ' ') && !mLineBuffer.empty())

-				{

-					const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y);

-					drawList->AddText(newOffset, prevColor, mLineBuffer.c_str());

-					auto textSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, mLineBuffer.c_str(), nullptr, nullptr);

-					bufferOffset.x += textSize.x;

-					mLineBuffer.clear();

-				}

-				prevColor = color;

-

-				if (glyph.mChar == '\t')

-				{

-					auto oldX = bufferOffset.x;

-					bufferOffset.x = (1.0f + std::floor((1.0f + bufferOffset.x) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);

-					++i;

-

-					if (mShowWhitespaces)

-					{

-						const auto s = ImGui::GetFontSize();

-						const auto x1 = textScreenPos.x + oldX + 1.0f;

-						const auto x2 = textScreenPos.x + bufferOffset.x - 1.0f;

-						const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f;

-						const ImVec2 p1(x1, y);

-						const ImVec2 p2(x2, y);

-						const ImVec2 p3(x2 - s * 0.2f, y - s * 0.2f);

-						const ImVec2 p4(x2 - s * 0.2f, y + s * 0.2f);

-						drawList->AddLine(p1, p2, 0x90909090);

-						drawList->AddLine(p2, p3, 0x90909090);

-						drawList->AddLine(p2, p4, 0x90909090);

-					}

-				}

-				else if (glyph.mChar == ' ')

-				{

-					if (mShowWhitespaces)

-					{

-						const auto s = ImGui::GetFontSize();

-						const auto x = textScreenPos.x + bufferOffset.x + spaceSize * 0.5f;

-						const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f;

-						drawList->AddCircleFilled(ImVec2(x, y), 1.5f, 0x80808080, 4);

-					}

-					bufferOffset.x += spaceSize;

-					i++;

-				}

-				else

-				{

-					auto l = UTF8CharLength(glyph.mChar);

-					while (l-- > 0)

-						mLineBuffer.push_back(line[i++].mChar);

-				}

-				++columnNo;

-			}

-

-			if (!mLineBuffer.empty())

-			{

-				const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y);

-				drawList->AddText(newOffset, prevColor, mLineBuffer.c_str());

-				mLineBuffer.clear();

-			}

-

-			++lineNo;

-		}

-

-		// Draw a tooltip on known identifiers/preprocessor symbols

-		if (ImGui::IsMousePosValid())

-		{

-			auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos()));

-			if (!id.empty())

-			{

-				auto it = mLanguageDefinition.mIdentifiers.find(id);

-				if (it != mLanguageDefinition.mIdentifiers.end())

-				{

-					ImGui::BeginTooltip();

-					ImGui::TextUnformatted(it->second.mDeclaration.c_str());

-					ImGui::EndTooltip();

-				}

-				else

-				{

-					auto pi = mLanguageDefinition.mPreprocIdentifiers.find(id);

-					if (pi != mLanguageDefinition.mPreprocIdentifiers.end())

-					{

-						ImGui::BeginTooltip();

-						ImGui::TextUnformatted(pi->second.mDeclaration.c_str());

-						ImGui::EndTooltip();

-					}

-				}

-			}

-		}

-	}

-

-

-	ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y));

-

-	if (mScrollToCursor)

-	{

-		EnsureCursorVisible();

-		ImGui::SetWindowFocus();

-		mScrollToCursor = false;

-	}

-}

-

-void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)

-{

-	mWithinRender = true;

-	mTextChanged = false;

-	mCursorPositionChanged = false;

-

-	ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background]));

-	ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));

-	if (!mIgnoreImGuiChild)

-		ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove);

-

-	if (mHandleKeyboardInputs)

-	{

-		HandleKeyboardInputs();

-		ImGui::PushAllowKeyboardFocus(true);

-	}

-

-	if (mHandleMouseInputs)

-		HandleMouseInputs();

-

-	ColorizeInternal();

-	Render();

-

-	if (mHandleKeyboardInputs)

-		ImGui::PopAllowKeyboardFocus();

-

-	if (!mIgnoreImGuiChild)

-		ImGui::EndChild();

-

-	ImGui::PopStyleVar();

-	ImGui::PopStyleColor();

-

-	mWithinRender = false;

-}

-

-void TextEditor::SetText(const std::string & aText)

-{

-	mLines.clear();

-	mLines.emplace_back(Line());

-	for (auto chr : aText)

-	{

-		if (chr == '\r')

-		{

-			// ignore the carriage return character

-		}

-		else if (chr == '\n')

-			mLines.emplace_back(Line());

-		else

-		{

-			mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default));

-		}

-	}

-

-	mTextChanged = true;

-	mScrollToTop = true;

-

-	mUndoBuffer.clear();

-	mUndoIndex = 0;

-

-	Colorize();

-}

-

-void TextEditor::SetTextLines(const std::vector<std::string> & aLines)

-{

-	mLines.clear();

-

-	if (aLines.empty())

-	{

-		mLines.emplace_back(Line());

-	}

-	else

-	{

-		mLines.resize(aLines.size());

-

-		for (size_t i = 0; i < aLines.size(); ++i)

-		{

-			const std::string & aLine = aLines[i];

-

-			mLines[i].reserve(aLine.size());

-			for (size_t j = 0; j < aLine.size(); ++j)

-				mLines[i].emplace_back(Glyph(aLine[j], PaletteIndex::Default));

-		}

-	}

-

-	mTextChanged = true;

-	mScrollToTop = true;

-

-	mUndoBuffer.clear();

-	mUndoIndex = 0;

-

-	Colorize();

-}

-

-void TextEditor::EnterCharacter(ImWchar aChar, bool aShift)

-{

-	assert(!mReadOnly);

-

-	UndoRecord u;

-

-	u.mBefore = mState;

-

-	if (HasSelection())

-	{

-		if (aChar == '\t' && mState.mSelectionStart.mLine != mState.mSelectionEnd.mLine)

-		{

-

-			auto start = mState.mSelectionStart;

-			auto end = mState.mSelectionEnd;

-			auto originalEnd = end;

-

-			if (start > end)

-				std::swap(start, end);

-			start.mColumn = 0;

-			//			end.mColumn = end.mLine < mLines.size() ? mLines[end.mLine].size() : 0;

-			if (end.mColumn == 0 && end.mLine > 0)

-				--end.mLine;

-			if (end.mLine >= (int)mLines.size())

-				end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1;

-			end.mColumn = GetLineMaxColumn(end.mLine);

-

-			//if (end.mColumn >= GetLineMaxColumn(end.mLine))

-			//	end.mColumn = GetLineMaxColumn(end.mLine) - 1;

-

-			u.mRemovedStart = start;

-			u.mRemovedEnd = end;

-			u.mRemoved = GetText(start, end);

-

-			bool modified = false;

-

-			for (int i = start.mLine; i <= end.mLine; i++)

-			{

-				auto& line = mLines[i];

-				if (aShift)

-				{

-					if (!line.empty())

-					{

-						if (line.front().mChar == '\t')

-						{

-							line.erase(line.begin());

-							modified = true;

-						}

-						else

-						{

-							for (int j = 0; j < mTabSize && !line.empty() && line.front().mChar == ' '; j++)

-							{

-								line.erase(line.begin());

-								modified = true;

-							}

-						}

-					}

-				}

-				else

-				{

-					line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background));

-					modified = true;

-				}

-			}

-

-			if (modified)

-			{

-				start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0));

-				Coordinates rangeEnd;

-				if (originalEnd.mColumn != 0)

-				{

-					end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine));

-					rangeEnd = end;

-					u.mAdded = GetText(start, end);

-				}

-				else

-				{

-					end = Coordinates(originalEnd.mLine, 0);

-					rangeEnd = Coordinates(end.mLine - 1, GetLineMaxColumn(end.mLine - 1));

-					u.mAdded = GetText(start, rangeEnd);

-				}

-

-				u.mAddedStart = start;

-				u.mAddedEnd = rangeEnd;

-				u.mAfter = mState;

-

-				mState.mSelectionStart = start;

-				mState.mSelectionEnd = end;

-				AddUndo(u);

-

-				mTextChanged = true;

-

-				EnsureCursorVisible();

-			}

-

-			return;

-		} // c == '\t'

-		else

-		{

-			u.mRemoved = GetSelectedText();

-			u.mRemovedStart = mState.mSelectionStart;

-			u.mRemovedEnd = mState.mSelectionEnd;

-			DeleteSelection();

-		}

-	} // HasSelection

-

-	auto coord = GetActualCursorCoordinates();

-	u.mAddedStart = coord;

-

-	assert(!mLines.empty());

-

-	if (aChar == '\n')

-	{

-		InsertLine(coord.mLine + 1);

-		auto& line = mLines[coord.mLine];

-		auto& newLine = mLines[coord.mLine + 1];

-

-		if (mLanguageDefinition.mAutoIndentation)

-			for (size_t it = 0; it < line.size() && isascii(line[it].mChar) && isblank(line[it].mChar); ++it)

-				newLine.push_back(line[it]);

-

-		const size_t whitespaceSize = newLine.size();

-		auto cindex = GetCharacterIndex(coord);

-		newLine.insert(newLine.end(), line.begin() + cindex, line.end());

-		line.erase(line.begin() + cindex, line.begin() + line.size());

-		SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize)));

-		u.mAdded = (char)aChar;

-	}

-	else

-	{

-		char buf[7];

-		int e = ImTextCharToUtf8(buf, 7, aChar);

-		if (e > 0)

-		{

-			buf[e] = '\0';

-			auto& line = mLines[coord.mLine];

-			auto cindex = GetCharacterIndex(coord);

-

-			if (mOverwrite && cindex < (int)line.size())

-			{

-				auto d = UTF8CharLength(line[cindex].mChar);

-

-				u.mRemovedStart = mState.mCursorPosition;

-				u.mRemovedEnd = Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + d));

-

-				while (d-- > 0 && cindex < (int)line.size())

-				{

-					u.mRemoved += line[cindex].mChar;

-					line.erase(line.begin() + cindex);

-				}

-			}

-

-			for (auto p = buf; *p != '\0'; p++, ++cindex)

-				line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default));

-			u.mAdded = buf;

-

-			SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex)));

-		}

-		else

-			return;

-	}

-

-	mTextChanged = true;

-

-	u.mAddedEnd = GetActualCursorCoordinates();

-	u.mAfter = mState;

-

-	AddUndo(u);

-

-	Colorize(coord.mLine - 1, 3);

-	EnsureCursorVisible();

-}

-

-void TextEditor::SetReadOnly(bool aValue)

-{

-	mReadOnly = aValue;

-}

-

-void TextEditor::SetColorizerEnable(bool aValue)

-{

-	mColorizerEnabled = aValue;

-}

-

-void TextEditor::SetCursorPosition(const Coordinates & aPosition)

-{

-	if (mState.mCursorPosition != aPosition)

-	{

-		mState.mCursorPosition = aPosition;

-		mCursorPositionChanged = true;

-		EnsureCursorVisible();

-	}

-}

-

-void TextEditor::SetSelectionStart(const Coordinates & aPosition)

-{

-	mState.mSelectionStart = SanitizeCoordinates(aPosition);

-	if (mState.mSelectionStart > mState.mSelectionEnd)

-		std::swap(mState.mSelectionStart, mState.mSelectionEnd);

-}

-

-void TextEditor::SetSelectionEnd(const Coordinates & aPosition)

-{

-	mState.mSelectionEnd = SanitizeCoordinates(aPosition);

-	if (mState.mSelectionStart > mState.mSelectionEnd)

-		std::swap(mState.mSelectionStart, mState.mSelectionEnd);

-}

-

-void TextEditor::SetSelection(const Coordinates & aStart, const Coordinates & aEnd, SelectionMode aMode)

-{

-	auto oldSelStart = mState.mSelectionStart;

-	auto oldSelEnd = mState.mSelectionEnd;

-

-	mState.mSelectionStart = SanitizeCoordinates(aStart);

-	mState.mSelectionEnd = SanitizeCoordinates(aEnd);

-	if (mState.mSelectionStart > mState.mSelectionEnd)

-		std::swap(mState.mSelectionStart, mState.mSelectionEnd);

-

-	switch (aMode)

-	{

-	case TextEditor::SelectionMode::Normal:

-		break;

-	case TextEditor::SelectionMode::Word:

-	{

-		mState.mSelectionStart = FindWordStart(mState.mSelectionStart);

-		if (!IsOnWordBoundary(mState.mSelectionEnd))

-			mState.mSelectionEnd = FindWordEnd(FindWordStart(mState.mSelectionEnd));

-		break;

-	}

-	case TextEditor::SelectionMode::Line:

-	{

-		const auto lineNo = mState.mSelectionEnd.mLine;

-		const auto lineSize = (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0;

-		mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0);

-		mState.mSelectionEnd = Coordinates(lineNo, GetLineMaxColumn(lineNo));

-		break;

-	}

-	default:

-		break;

-	}

-

-	if (mState.mSelectionStart != oldSelStart ||

-		mState.mSelectionEnd != oldSelEnd)

-		mCursorPositionChanged = true;

-}

-

-void TextEditor::SetTabSize(int aValue)

-{

-	mTabSize = std::max(0, std::min(32, aValue));

-}

-

-void TextEditor::InsertText(const std::string & aValue)

-{

-	InsertText(aValue.c_str());

-}

-

-void TextEditor::InsertText(const char * aValue)

-{

-	if (aValue == nullptr)

-		return;

-

-	auto pos = GetActualCursorCoordinates();

-	auto start = std::min(pos, mState.mSelectionStart);

-	int totalLines = pos.mLine - start.mLine;

-

-	totalLines += InsertTextAt(pos, aValue);

-

-	SetSelection(pos, pos);

-	SetCursorPosition(pos);

-	Colorize(start.mLine - 1, totalLines + 2);

-}

-

-void TextEditor::DeleteSelection()

-{

-	assert(mState.mSelectionEnd >= mState.mSelectionStart);

-

-	if (mState.mSelectionEnd == mState.mSelectionStart)

-		return;

-

-	DeleteRange(mState.mSelectionStart, mState.mSelectionEnd);

-

-	SetSelection(mState.mSelectionStart, mState.mSelectionStart);

-	SetCursorPosition(mState.mSelectionStart);

-	Colorize(mState.mSelectionStart.mLine, 1);

-}

-

-void TextEditor::MoveUp(int aAmount, bool aSelect)

-{

-	auto oldPos = mState.mCursorPosition;

-	mState.mCursorPosition.mLine = std::max(0, mState.mCursorPosition.mLine - aAmount);

-	if (oldPos != mState.mCursorPosition)

-	{

-		if (aSelect)

-		{

-			if (oldPos == mInteractiveStart)

-				mInteractiveStart = mState.mCursorPosition;

-			else if (oldPos == mInteractiveEnd)

-				mInteractiveEnd = mState.mCursorPosition;

-			else

-			{

-				mInteractiveStart = mState.mCursorPosition;

-				mInteractiveEnd = oldPos;

-			}

-		}

-		else

-			mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;

-		SetSelection(mInteractiveStart, mInteractiveEnd);

-

-		EnsureCursorVisible();

-	}

-}

-

-void TextEditor::MoveDown(int aAmount, bool aSelect)

-{

-	assert(mState.mCursorPosition.mColumn >= 0);

-	auto oldPos = mState.mCursorPosition;

-	mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + aAmount));

-

-	if (mState.mCursorPosition != oldPos)

-	{

-		if (aSelect)

-		{

-			if (oldPos == mInteractiveEnd)

-				mInteractiveEnd = mState.mCursorPosition;

-			else if (oldPos == mInteractiveStart)

-				mInteractiveStart = mState.mCursorPosition;

-			else

-			{

-				mInteractiveStart = oldPos;

-				mInteractiveEnd = mState.mCursorPosition;

-			}

-		}

-		else

-			mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;

-		SetSelection(mInteractiveStart, mInteractiveEnd);

-

-		EnsureCursorVisible();

-	}

-}

-

-static bool IsUTFSequence(char c)

-{

-	return (c & 0xC0) == 0x80;

-}

-

-void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode)

-{

-	if (mLines.empty())

-		return;

-

-	auto oldPos = mState.mCursorPosition;

-	mState.mCursorPosition = GetActualCursorCoordinates();

-	auto line = mState.mCursorPosition.mLine;

-	auto cindex = GetCharacterIndex(mState.mCursorPosition);

-

-	while (aAmount-- > 0)

-	{

-		if (cindex == 0)

-		{

-			if (line > 0)

-			{

-				--line;

-				if ((int)mLines.size() > line)

-					cindex = (int)mLines[line].size();

-				else

-					cindex = 0;

-			}

-		}

-		else

-		{

-			--cindex;

-			if (cindex > 0)

-			{

-				if ((int)mLines.size() > line)

-				{

-					while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar))

-						--cindex;

-				}

-			}

-		}

-

-		mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex));

-		if (aWordMode)

-		{

-			mState.mCursorPosition = FindWordStart(mState.mCursorPosition);

-			cindex = GetCharacterIndex(mState.mCursorPosition);

-		}

-	}

-

-	mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex));

-

-	assert(mState.mCursorPosition.mColumn >= 0);

-	if (aSelect)

-	{

-		if (oldPos == mInteractiveStart)

-			mInteractiveStart = mState.mCursorPosition;

-		else if (oldPos == mInteractiveEnd)

-			mInteractiveEnd = mState.mCursorPosition;

-		else

-		{

-			mInteractiveStart = mState.mCursorPosition;

-			mInteractiveEnd = oldPos;

-		}

-	}

-	else

-		mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;

-	SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal);

-

-	EnsureCursorVisible();

-}

-

-void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode)

-{

-	auto oldPos = mState.mCursorPosition;

-

-	if (mLines.empty() || oldPos.mLine >= mLines.size())

-		return;

-

-	auto cindex = GetCharacterIndex(mState.mCursorPosition);

-	while (aAmount-- > 0)

-	{

-		auto lindex = mState.mCursorPosition.mLine;

-		auto& line = mLines[lindex];

-

-		if (cindex >= line.size())

-		{

-			if (mState.mCursorPosition.mLine < mLines.size() - 1)

-			{

-				mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + 1));

-				mState.mCursorPosition.mColumn = 0;

-			}

-			else

-				return;

-		}

-		else

-		{

-			cindex += UTF8CharLength(line[cindex].mChar);

-			mState.mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex));

-			if (aWordMode)

-				mState.mCursorPosition = FindNextWord(mState.mCursorPosition);

-		}

-	}

-

-	if (aSelect)

-	{

-		if (oldPos == mInteractiveEnd)

-			mInteractiveEnd = SanitizeCoordinates(mState.mCursorPosition);

-		else if (oldPos == mInteractiveStart)

-			mInteractiveStart = mState.mCursorPosition;

-		else

-		{

-			mInteractiveStart = oldPos;

-			mInteractiveEnd = mState.mCursorPosition;

-		}

-	}

-	else

-		mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;

-	SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal);

-

-	EnsureCursorVisible();

-}

-

-void TextEditor::MoveTop(bool aSelect)

-{

-	auto oldPos = mState.mCursorPosition;

-	SetCursorPosition(Coordinates(0, 0));

-

-	if (mState.mCursorPosition != oldPos)

-	{

-		if (aSelect)

-		{

-			mInteractiveEnd = oldPos;

-			mInteractiveStart = mState.mCursorPosition;

-		}

-		else

-			mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;

-		SetSelection(mInteractiveStart, mInteractiveEnd);

-	}

-}

-

-void TextEditor::TextEditor::MoveBottom(bool aSelect)

-{

-	auto oldPos = GetCursorPosition();

-	auto newPos = Coordinates((int)mLines.size() - 1, 0);

-	SetCursorPosition(newPos);

-	if (aSelect)

-	{

-		mInteractiveStart = oldPos;

-		mInteractiveEnd = newPos;

-	}

-	else

-		mInteractiveStart = mInteractiveEnd = newPos;

-	SetSelection(mInteractiveStart, mInteractiveEnd);

-}

-

-void TextEditor::MoveHome(bool aSelect)

-{

-	auto oldPos = mState.mCursorPosition;

-	SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, 0));

-

-	if (mState.mCursorPosition != oldPos)

-	{

-		if (aSelect)

-		{

-			if (oldPos == mInteractiveStart)

-				mInteractiveStart = mState.mCursorPosition;

-			else if (oldPos == mInteractiveEnd)

-				mInteractiveEnd = mState.mCursorPosition;

-			else

-			{

-				mInteractiveStart = mState.mCursorPosition;

-				mInteractiveEnd = oldPos;

-			}

-		}

-		else

-			mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;

-		SetSelection(mInteractiveStart, mInteractiveEnd);

-	}

-}

-

-void TextEditor::MoveEnd(bool aSelect)

-{

-	auto oldPos = mState.mCursorPosition;

-	SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, GetLineMaxColumn(oldPos.mLine)));

-

-	if (mState.mCursorPosition != oldPos)

-	{

-		if (aSelect)

-		{

-			if (oldPos == mInteractiveEnd)

-				mInteractiveEnd = mState.mCursorPosition;

-			else if (oldPos == mInteractiveStart)

-				mInteractiveStart = mState.mCursorPosition;

-			else

-			{

-				mInteractiveStart = oldPos;

-				mInteractiveEnd = mState.mCursorPosition;

-			}

-		}

-		else

-			mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;

-		SetSelection(mInteractiveStart, mInteractiveEnd);

-	}

-}

-

-void TextEditor::Delete()

-{

-	assert(!mReadOnly);

-

-	if (mLines.empty())

-		return;

-

-	UndoRecord u;

-	u.mBefore = mState;

-

-	if (HasSelection())

-	{

-		u.mRemoved = GetSelectedText();

-		u.mRemovedStart = mState.mSelectionStart;

-		u.mRemovedEnd = mState.mSelectionEnd;

-

-		DeleteSelection();

-	}

-	else

-	{

-		auto pos = GetActualCursorCoordinates();

-		SetCursorPosition(pos);

-		auto& line = mLines[pos.mLine];

-

-		if (pos.mColumn == GetLineMaxColumn(pos.mLine))

-		{

-			if (pos.mLine == (int)mLines.size() - 1)

-				return;

-

-			u.mRemoved = '\n';

-			u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates();

-			Advance(u.mRemovedEnd);

-

-			auto& nextLine = mLines[pos.mLine + 1];

-			line.insert(line.end(), nextLine.begin(), nextLine.end());

-			RemoveLine(pos.mLine + 1);

-		}

-		else

-		{

-			auto cindex = GetCharacterIndex(pos);

-			u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates();

-			u.mRemovedEnd.mColumn++;

-			u.mRemoved = GetText(u.mRemovedStart, u.mRemovedEnd);

-

-			auto d = UTF8CharLength(line[cindex].mChar);

-			while (d-- > 0 && cindex < (int)line.size())

-				line.erase(line.begin() + cindex);

-		}

-

-		mTextChanged = true;

-

-		Colorize(pos.mLine, 1);

-	}

-

-	u.mAfter = mState;

-	AddUndo(u);

-}

-

-void TextEditor::Backspace()

-{

-	assert(!mReadOnly);

-

-	if (mLines.empty())

-		return;

-

-	UndoRecord u;

-	u.mBefore = mState;

-

-	if (HasSelection())

-	{

-		u.mRemoved = GetSelectedText();

-		u.mRemovedStart = mState.mSelectionStart;

-		u.mRemovedEnd = mState.mSelectionEnd;

-

-		DeleteSelection();

-	}

-	else

-	{

-		auto pos = GetActualCursorCoordinates();

-		SetCursorPosition(pos);

-

-		if (mState.mCursorPosition.mColumn == 0)

-		{

-			if (mState.mCursorPosition.mLine == 0)

-				return;

-

-			u.mRemoved = '\n';

-			u.mRemovedStart = u.mRemovedEnd = Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1));

-			Advance(u.mRemovedEnd);

-

-			auto& line = mLines[mState.mCursorPosition.mLine];

-			auto& prevLine = mLines[mState.mCursorPosition.mLine - 1];

-			auto prevSize = GetLineMaxColumn(mState.mCursorPosition.mLine - 1);

-			prevLine.insert(prevLine.end(), line.begin(), line.end());

-

-			ErrorMarkers etmp;

-			for (auto& i : mErrorMarkers)

-				etmp.insert(ErrorMarkers::value_type(i.first - 1 == mState.mCursorPosition.mLine ? i.first - 1 : i.first, i.second));

-			mErrorMarkers = std::move(etmp);

-

-			RemoveLine(mState.mCursorPosition.mLine);

-			--mState.mCursorPosition.mLine;

-			mState.mCursorPosition.mColumn = prevSize;

-		}

-		else

-		{

-			auto& line = mLines[mState.mCursorPosition.mLine];

-			auto cindex = GetCharacterIndex(pos) - 1;

-			auto cend = cindex + 1;

-			while (cindex > 0 && IsUTFSequence(line[cindex].mChar))

-				--cindex;

-

-			//if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1)

-			//	--cindex;

-

-			u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates();

-			--u.mRemovedStart.mColumn;

-			--mState.mCursorPosition.mColumn;

-

-			while (cindex < line.size() && cend-- > cindex)

-			{

-				u.mRemoved += line[cindex].mChar;

-				line.erase(line.begin() + cindex);

-			}

-		}

-

-		mTextChanged = true;

-

-		EnsureCursorVisible();

-		Colorize(mState.mCursorPosition.mLine, 1);

-	}

-

-	u.mAfter = mState;

-	AddUndo(u);

-}

-

-void TextEditor::SelectWordUnderCursor()

-{

-	auto c = GetCursorPosition();

-	SetSelection(FindWordStart(c), FindWordEnd(c));

-}

-

-void TextEditor::SelectAll()

-{

-	SetSelection(Coordinates(0, 0), Coordinates((int)mLines.size(), 0));

-}

-

-bool TextEditor::HasSelection() const

-{

-	return mState.mSelectionEnd > mState.mSelectionStart;

-}

-

-void TextEditor::Copy()

-{

-	if (HasSelection())

-	{

-		ImGui::SetClipboardText(GetSelectedText().c_str());

-	}

-	else

-	{

-		if (!mLines.empty())

-		{

-			std::string str;

-			auto& line = mLines[GetActualCursorCoordinates().mLine];

-			for (auto& g : line)

-				str.push_back(g.mChar);

-			ImGui::SetClipboardText(str.c_str());

-		}

-	}

-}

-

-void TextEditor::Cut()

-{

-	if (IsReadOnly())

-	{

-		Copy();

-	}

-	else

-	{

-		if (HasSelection())

-		{

-			UndoRecord u;

-			u.mBefore = mState;

-			u.mRemoved = GetSelectedText();

-			u.mRemovedStart = mState.mSelectionStart;

-			u.mRemovedEnd = mState.mSelectionEnd;

-

-			Copy();

-			DeleteSelection();

-

-			u.mAfter = mState;

-			AddUndo(u);

-		}

-	}

-}

-

-void TextEditor::Paste()

-{

-	if (IsReadOnly())

-		return;

-

-	auto clipText = ImGui::GetClipboardText();

-	if (clipText != nullptr && strlen(clipText) > 0)

-	{

-		UndoRecord u;

-		u.mBefore = mState;

-

-		if (HasSelection())

-		{

-			u.mRemoved = GetSelectedText();

-			u.mRemovedStart = mState.mSelectionStart;

-			u.mRemovedEnd = mState.mSelectionEnd;

-			DeleteSelection();

-		}

-

-		u.mAdded = clipText;

-		u.mAddedStart = GetActualCursorCoordinates();

-

-		InsertText(clipText);

-

-		u.mAddedEnd = GetActualCursorCoordinates();

-		u.mAfter = mState;

-		AddUndo(u);

-	}

-}

-

-bool TextEditor::CanUndo() const

-{

-	return !mReadOnly && mUndoIndex > 0;

-}

-

-bool TextEditor::CanRedo() const

-{

-	return !mReadOnly && mUndoIndex < (int)mUndoBuffer.size();

-}

-

-void TextEditor::Undo(int aSteps)

-{

-	while (CanUndo() && aSteps-- > 0)

-		mUndoBuffer[--mUndoIndex].Undo(this);

-}

-

-void TextEditor::Redo(int aSteps)

-{

-	while (CanRedo() && aSteps-- > 0)

-		mUndoBuffer[mUndoIndex++].Redo(this);

-}

-

-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

-			0xff206020, // Comment (single line)

-			0xff406020, // Comment (multi line)

-			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

-			0xff205020, // Comment (single line)

-			0xff405020, // Comment (multi line)

-			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

-			0xff808080, // Comment (single line)

-			0xff404040, // Comment (multi line)

-			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

-{

-	return GetText(Coordinates(), Coordinates((int)mLines.size(), 0));

-}

-

-std::vector<std::string> TextEditor::GetTextLines() const

-{

-	std::vector<std::string> result;

-

-	result.reserve(mLines.size());

-

-	for (auto & line : mLines)

-	{

-		std::string text;

-

-		text.resize(line.size());

-

-		for (size_t i = 0; i < line.size(); ++i)

-			text[i] = line[i].mChar;

-

-		result.emplace_back(std::move(text));

-	}

-

-	return result;

-}

-

-std::string TextEditor::GetSelectedText() const

-{

-	return GetText(mState.mSelectionStart, mState.mSelectionEnd);

-}

-

-std::string TextEditor::GetCurrentLineText()const

-{

-	auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine);

-	return GetText(

-		Coordinates(mState.mCursorPosition.mLine, 0),

-		Coordinates(mState.mCursorPosition.mLine, lineLength));

-}

-

-void TextEditor::ProcessInputs()

-{

-}

-

-void TextEditor::Colorize(int aFromLine, int aLines)

-{

-	int toLine = aLines == -1 ? (int)mLines.size() : std::min((int)mLines.size(), aFromLine + aLines);

-	mColorRangeMin = std::min(mColorRangeMin, aFromLine);

-	mColorRangeMax = std::max(mColorRangeMax, toLine);

-	mColorRangeMin = std::max(0, mColorRangeMin);

-	mColorRangeMax = std::max(mColorRangeMin, mColorRangeMax);

-	mCheckComments = true;

-}

-

-void TextEditor::ColorizeRange(int aFromLine, int aToLine)

-{

-	if (mLines.empty() || aFromLine >= aToLine)

-		return;

-

-	std::string buffer;

-	std::cmatch results;

-	std::string id;

-

-	int endLine = std::max(0, std::min((int)mLines.size(), aToLine));

-	for (int i = aFromLine; i < endLine; ++i)

-	{

-		auto& line = mLines[i];

-

-		if (line.empty())

-			continue;

-

-		buffer.resize(line.size());

-		for (size_t j = 0; j < line.size(); ++j)

-		{

-			auto& col = line[j];

-			buffer[j] = col.mChar;

-			col.mColorIndex = PaletteIndex::Default;

-		}

-

-		const char * bufferBegin = &buffer.front();

-		const char * bufferEnd = bufferBegin + buffer.size();

-

-		auto last = bufferEnd;

-

-		for (auto first = bufferBegin; first != last; )

-		{

-			const char * token_begin = nullptr;

-			const char * token_end = nullptr;

-			PaletteIndex token_color = PaletteIndex::Default;

-

-			bool hasTokenizeResult = false;

-

-			if (mLanguageDefinition.mTokenize != nullptr)

-			{

-				if (mLanguageDefinition.mTokenize(first, last, token_begin, token_end, token_color))

-					hasTokenizeResult = true;

-			}

-

-			if (hasTokenizeResult == false)

-			{

-				// todo : remove

-				//printf("using regex for %.*s\n", first + 10 < last ? 10 : int(last - first), first);

-

-				for (auto& p : mRegexList)

-				{

-					if (std::regex_search(first, last, results, p.first, std::regex_constants::match_continuous))

-					{

-						hasTokenizeResult = true;

-

-						auto& v = *results.begin();

-						token_begin = v.first;

-						token_end = v.second;

-						token_color = p.second;

-						break;

-					}

-				}

-			}

-

-			if (hasTokenizeResult == false)

-			{

-				first++;

-			}

-			else

-			{

-				const size_t token_length = token_end - token_begin;

-

-				if (token_color == PaletteIndex::Identifier)

-				{

-					id.assign(token_begin, token_end);

-

-					// todo : allmost all language definitions use lower case to specify keywords, so shouldn't this use ::tolower ?

-					if (!mLanguageDefinition.mCaseSensitive)

-						std::transform(id.begin(), id.end(), id.begin(), ::toupper);

-

-					if (!line[first - bufferBegin].mPreprocessor)

-					{

-						if (mLanguageDefinition.mKeywords.count(id) != 0)

-							token_color = PaletteIndex::Keyword;

-						else if (mLanguageDefinition.mIdentifiers.count(id) != 0)

-							token_color = PaletteIndex::KnownIdentifier;

-						else if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0)

-							token_color = PaletteIndex::PreprocIdentifier;

-					}

-					else

-					{

-						if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0)

-							token_color = PaletteIndex::PreprocIdentifier;

-					}

-				}

-

-				for (size_t j = 0; j < token_length; ++j)

-					line[(token_begin - bufferBegin) + j].mColorIndex = token_color;

-

-				first = token_end;

-			}

-		}

-	}

-}

-

-void TextEditor::ColorizeInternal()

-{

-	if (mLines.empty() || !mColorizerEnabled)

-		return;

-

-	if (mCheckComments)

-	{

-		auto endLine = mLines.size();

-		auto endIndex = 0;

-		auto commentStartLine = endLine;

-		auto commentStartIndex = endIndex;

-		auto withinString = false;

-		auto withinSingleLineComment = false;

-		auto withinPreproc = false;

-		auto firstChar = true;			// there is no other non-whitespace characters in the line before

-		auto concatenate = false;		// '\' on the very end of the line

-		auto currentLine = 0;

-		auto currentIndex = 0;

-		while (currentLine < endLine || currentIndex < endIndex)

-		{

-			auto& line = mLines[currentLine];

-

-			if (currentIndex == 0 && !concatenate)

-			{

-				withinSingleLineComment = false;

-				withinPreproc = false;

-				firstChar = true;

-			}

-

-			concatenate = false;

-

-			if (!line.empty())

-			{

-				auto& g = line[currentIndex];

-				auto c = g.mChar;

-

-				if (c != mLanguageDefinition.mPreprocChar && !isspace(c))

-					firstChar = false;

-

-				if (currentIndex == (int)line.size() - 1 && line[line.size() - 1].mChar == '\\')

-					concatenate = true;

-

-				bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));

-

-				if (withinString)

-				{

-					line[currentIndex].mMultiLineComment = inComment;

-

-					if (c == '\"')

-					{

-						if (currentIndex + 1 < (int)line.size() && line[currentIndex + 1].mChar == '\"')

-						{

-							currentIndex += 1;

-							if (currentIndex < (int)line.size())

-								line[currentIndex].mMultiLineComment = inComment;

-						}

-						else

-							withinString = false;

-					}

-					else if (c == '\\')

-					{

-						currentIndex += 1;

-						if (currentIndex < (int)line.size())

-							line[currentIndex].mMultiLineComment = inComment;

-					}

-				}

-				else

-				{

-					if (firstChar && c == mLanguageDefinition.mPreprocChar)

-						withinPreproc = true;

-

-					if (c == '\"')

-					{

-						withinString = true;

-						line[currentIndex].mMultiLineComment = inComment;

-					}

-					else

-					{

-						auto pred = [](const char& a, const Glyph& b) { return a == b.mChar; };

-						auto from = line.begin() + currentIndex;

-						auto& startStr = mLanguageDefinition.mCommentStart;

-						auto& singleStartStr = mLanguageDefinition.mSingleLineComment;

-

-						if (singleStartStr.size() > 0 &&

-							currentIndex + singleStartStr.size() <= line.size() &&

-							equals(singleStartStr.begin(), singleStartStr.end(), from, from + singleStartStr.size(), pred))

-						{

-							withinSingleLineComment = true;

-						}

-						else if (!withinSingleLineComment && currentIndex + startStr.size() <= line.size() &&

-							equals(startStr.begin(), startStr.end(), from, from + startStr.size(), pred))

-						{

-							commentStartLine = currentLine;

-							commentStartIndex = currentIndex;

-						}

-

-						inComment = inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));

-

-						line[currentIndex].mMultiLineComment = inComment;

-						line[currentIndex].mComment = withinSingleLineComment;

-

-						auto& endStr = mLanguageDefinition.mCommentEnd;

-						if (currentIndex + 1 >= (int)endStr.size() &&

-							equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(), from + 1, pred))

-						{

-							commentStartIndex = endIndex;

-							commentStartLine = endLine;

-						}

-					}

-				}

-				line[currentIndex].mPreprocessor = withinPreproc;

-				currentIndex += UTF8CharLength(c);

-				if (currentIndex >= (int)line.size())

-				{

-					currentIndex = 0;

-					++currentLine;

-				}

-			}

-			else

-			{

-				currentIndex = 0;

-				++currentLine;

-			}

-		}

-		mCheckComments = false;

-	}

-

-	if (mColorRangeMin < mColorRangeMax)

-	{

-		const int increment = (mLanguageDefinition.mTokenize == nullptr) ? 10 : 10000;

-		const int to = std::min(mColorRangeMin + increment, mColorRangeMax);

-		ColorizeRange(mColorRangeMin, to);

-		mColorRangeMin = to;

-

-		if (mColorRangeMax == mColorRangeMin)

-		{

-			mColorRangeMin = std::numeric_limits<int>::max();

-			mColorRangeMax = 0;

-		}

-		return;

-	}

-}

-

-float TextEditor::TextDistanceToLineStart(const Coordinates& aFrom) const

-{

-	auto& line = mLines[aFrom.mLine];

-	float distance = 0.0f;

-	float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x;

-	int colIndex = GetCharacterIndex(aFrom);

-	for (size_t it = 0u; it < line.size() && it < colIndex; )

-	{

-		if (line[it].mChar == '\t')

-		{

-			distance = (1.0f + std::floor((1.0f + distance) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);

-			++it;

-		}

-		else

-		{

-			auto d = UTF8CharLength(line[it].mChar);

-			char tempCString[7];

-			int i = 0;

-			for (; i < 6 && d-- > 0 && it < (int)line.size(); i++, it++)

-				tempCString[i] = line[it].mChar;

-

-			tempCString[i] = '\0';

-			distance += ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, tempCString, nullptr, nullptr).x;

-		}

-	}

-

-	return distance;

-}

-

-void TextEditor::EnsureCursorVisible()

-{

-	if (!mWithinRender)

-	{

-		mScrollToCursor = true;

-		return;

-	}

-

-	float scrollX = ImGui::GetScrollX();

-	float scrollY = ImGui::GetScrollY();

-

-	auto height = ImGui::GetWindowHeight();

-	auto width = ImGui::GetWindowWidth();

-

-	auto top = 1 + (int)ceil(scrollY / mCharAdvance.y);

-	auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y);

-

-	auto left = (int)ceil(scrollX / mCharAdvance.x);

-	auto right = (int)ceil((scrollX + width) / mCharAdvance.x);

-

-	auto pos = GetActualCursorCoordinates();

-	auto len = TextDistanceToLineStart(pos);

-

-	if (pos.mLine < top)

-		ImGui::SetScrollY(std::max(0.0f, (pos.mLine - 1) * mCharAdvance.y));

-	if (pos.mLine > bottom - 4)

-		ImGui::SetScrollY(std::max(0.0f, (pos.mLine + 4) * mCharAdvance.y - height));

-	if (len + mTextStart < left + 4)

-		ImGui::SetScrollX(std::max(0.0f, len + mTextStart - 4));

-	if (len + mTextStart > right - 4)

-		ImGui::SetScrollX(std::max(0.0f, len + mTextStart + 4 - width));

-}

-

-int TextEditor::GetPageSize() const

-{

-	auto height = ImGui::GetWindowHeight() - 20.0f;

-	return (int)floor(height / mCharAdvance.y);

-}

-

-TextEditor::UndoRecord::UndoRecord(

-	const std::string& aAdded,

-	const TextEditor::Coordinates aAddedStart,

-	const TextEditor::Coordinates aAddedEnd,

-	const std::string& aRemoved,

-	const TextEditor::Coordinates aRemovedStart,

-	const TextEditor::Coordinates aRemovedEnd,

-	TextEditor::EditorState& aBefore,

-	TextEditor::EditorState& aAfter)

-	: mAdded(aAdded)

-	, mAddedStart(aAddedStart)

-	, mAddedEnd(aAddedEnd)

-	, mRemoved(aRemoved)

-	, mRemovedStart(aRemovedStart)

-	, mRemovedEnd(aRemovedEnd)

-	, mBefore(aBefore)

-	, mAfter(aAfter)

-{

-	assert(mAddedStart <= mAddedEnd);

-	assert(mRemovedStart <= mRemovedEnd);

-}

-

-void TextEditor::UndoRecord::Undo(TextEditor * aEditor)

-{

-	if (!mAdded.empty())

-	{

-		aEditor->DeleteRange(mAddedStart, mAddedEnd);

-		aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 2);

-	}

-

-	if (!mRemoved.empty())

-	{

-		auto start = mRemovedStart;

-		aEditor->InsertTextAt(start, mRemoved.c_str());

-		aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 2);

-	}

-

-	aEditor->mState = mBefore;

-	aEditor->EnsureCursorVisible();

-

-}

-

-void TextEditor::UndoRecord::Redo(TextEditor * aEditor)

-{

-	if (!mRemoved.empty())

-	{

-		aEditor->DeleteRange(mRemovedStart, mRemovedEnd);

-		aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 1);

-	}

-

-	if (!mAdded.empty())

-	{

-		auto start = mAddedStart;

-		aEditor->InsertTextAt(start, mAdded.c_str());

-		aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 1);

-	}

-

-	aEditor->mState = mAfter;

-	aEditor->EnsureCursorVisible();

-}

-

-static bool TokenizeCStyleString(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)

-{

-	const char * 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++;

-

-			p++;

-		}

-	}

-

-	return false;

-}

-

-static bool TokenizeCStyleCharacterLiteral(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)

-{

-	const char * 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;

-			return true;

-		}

-	}

-

-	return false;

-}

-

-static bool TokenizeCStyleIdentifier(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)

-{

-	const char * p = in_begin;

-

-	if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_')

-	{

-		p++;

-

-		while ((p < in_end) && ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_'))

-			p++;

-

-		out_begin = in_begin;

-		out_end = p;

-		return true;

-	}

-

-	return false;

-}

-

-static bool TokenizeCStyleNumber(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)

-{

-	const char * p = in_begin;

-

-	const bool startsWithNumber = *p >= '0' && *p <= '9';

-

-	if (*p != '+' && *p != '-' && !startsWithNumber)

-		return false;

-

-	p++;

-

-	bool hasNumber = startsWithNumber;

-

-	while (p < in_end && (*p >= '0' && *p <= '9'))

-	{

-		hasNumber = true;

-

-		p++;

-	}

-

-	if (hasNumber == false)

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

-		}

-		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 == false && isBinary == false)

-	{

-		// 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 == false)

-				return false;

-		}

-

-		// single precision floating point type

-		if (p < in_end && *p == 'f')

-			p++;

-	}

-

-	if (isFloat == false)

-	{

-		// integer size type

-		while (p < in_end && (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L'))

-			p++;

-	}

-

-	out_begin = in_begin;

-	out_end = p;

-	return true;

-}

-

-static bool TokenizeCStylePunctuation(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)

-{

-	(void)in_end;

-

-	switch (*in_begin)

-	{

-	case '[':

-	case ']':

-	case '{':

-	case '}':

-	case '!':

-	case '%':

-	case '^':

-	case '&':

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

-}

-

-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.mKeywords.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.mDeclaration = "Built-in function";

-			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));

-		}

-

-		langDef.mTokenize = [](const char * in_begin, const char * in_end, const char *& out_begin, const char *& 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::String;

-			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::Number;

-			else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end))

-				paletteIndex = PaletteIndex::Punctuation;

-

-			return paletteIndex != PaletteIndex::Max;

-		};

-

-		langDef.mCommentStart = "/*";

-		langDef.mCommentEnd = "*/";

-		langDef.mSingleLineComment = "//";

-

-		langDef.mCaseSensitive = true;

-		langDef.mAutoIndentation = true;

-

-		langDef.mName = "C++";

-

-		inited = true;

-	}

-	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.mKeywords.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.mDeclaration = "Built-in function";

-			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));

-		}

-

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));

-

-		langDef.mCommentStart = "/*";

-		langDef.mCommentEnd = "*/";

-		langDef.mSingleLineComment = "//";

-

-		langDef.mCaseSensitive = true;

-		langDef.mAutoIndentation = true;

-

-		langDef.mName = "HLSL";

-

-		inited = true;

-	}

-	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.mKeywords.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.mDeclaration = "Built-in function";

-			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));

-		}

-

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));

-

-		langDef.mCommentStart = "/*";

-		langDef.mCommentEnd = "*/";

-		langDef.mSingleLineComment = "//";

-

-		langDef.mCaseSensitive = true;

-		langDef.mAutoIndentation = true;

-

-		langDef.mName = "GLSL";

-

-		inited = true;

-	}

-	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.mKeywords.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.mDeclaration = "Built-in function";

-			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));

-		}

-

-		langDef.mTokenize = [](const char * in_begin, const char * in_end, const char *& out_begin, const char *& 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::String;

-			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::Number;

-			else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end))

-				paletteIndex = PaletteIndex::Punctuation;

-

-			return paletteIndex != PaletteIndex::Max;

-		};

-

-		langDef.mCommentStart = "/*";

-		langDef.mCommentEnd = "*/";

-		langDef.mSingleLineComment = "//";

-

-		langDef.mCaseSensitive = true;

-		langDef.mAutoIndentation = true;

-

-		langDef.mName = "C";

-

-		inited = true;

-	}

-	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"

-		};

-

-		for (auto& k : keywords)

-			langDef.mKeywords.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.mDeclaration = "Built-in function";

-			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));

-		}

-

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'", PaletteIndex::String));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));

-

-		langDef.mCommentStart = "/*";

-		langDef.mCommentEnd = "*/";

-		langDef.mSingleLineComment = "//";

-

-		langDef.mCaseSensitive = false;

-		langDef.mAutoIndentation = false;

-

-		langDef.mName = "SQL";

-

-		inited = true;

-	}

-	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"

-		};

-

-		for (auto& k : keywords)

-			langDef.mKeywords.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.mDeclaration = "Built-in function";

-			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));

-		}

-

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::String));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));

-

-		langDef.mCommentStart = "/*";

-		langDef.mCommentEnd = "*/";

-		langDef.mSingleLineComment = "//";

-

-		langDef.mCaseSensitive = true;

-		langDef.mAutoIndentation = true;

-

-		langDef.mName = "AngelScript";

-

-		inited = true;

-	}

-	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.mKeywords.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.mDeclaration = "Built-in function";

-			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));

-		}

-

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'", PaletteIndex::String));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));

-		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));

-

-		langDef.mCommentStart = "--[[";

-		langDef.mCommentEnd = "]]";

-		langDef.mSingleLineComment = "--";

-

-		langDef.mCaseSensitive = true;

-		langDef.mAutoIndentation = false;

-

-		langDef.mName = "Lua";

-

-		inited = true;

-	}

-	return langDef;

-}

+#include <algorithm>
+#include <chrono>
+#include <string>
+#include <regex>
+#include <cmath>
+
+#include "TextEditor.h"
+
+#define IMGUI_DEFINE_MATH_OPERATORS
+#include "imgui.h" // for imGui::GetCurrentWindow()
+
+// TODO
+// - multiline comments vs single-line: latter is blocking start of a ML
+
+template<class InputIt1, class InputIt2, class BinaryPredicate>
+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;
+}
+
+TextEditor::TextEditor()
+	: mLineSpacing(1.0f)
+	, mUndoIndex(0)
+	, mTabSize(4)
+	, mOverwrite(false)
+	, mReadOnly(false)
+	, mWithinRender(false)
+	, mScrollToCursor(false)
+	, mScrollToTop(false)
+	, mTextChanged(false)
+	, mColorizerEnabled(true)
+	, mTextStart(20.0f)
+	, mLeftMargin(10)
+	, mCursorPositionChanged(false)
+	, mColorRangeMin(0)
+	, mColorRangeMax(0)
+	, mSelectionMode(SelectionMode::Normal)
+	, mCheckComments(true)
+	, mLastClick(-1.0f)
+	, mHandleKeyboardInputs(true)
+	, mHandleMouseInputs(true)
+	, mIgnoreImGuiChild(false)
+	, mShowWhitespaces(true)
+	, mStartTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
+{
+	SetPalette(GetDarkPalette());
+	SetLanguageDefinition(LanguageDefinition::HLSL());
+	mLines.push_back(Line());
+}
+
+TextEditor::~TextEditor()
+{
+}
+
+void TextEditor::SetLanguageDefinition(const LanguageDefinition & aLanguageDef)
+{
+	mLanguageDefinition = aLanguageDef;
+	mRegexList.clear();
+
+	for (auto& r : mLanguageDefinition.mTokenRegexStrings)
+		mRegexList.push_back(std::make_pair(std::regex(r.first, std::regex_constants::optimize), r.second));
+
+	Colorize();
+}
+
+void TextEditor::SetPalette(const Palette & aValue)
+{
+	mPaletteBase = aValue;
+}
+
+std::string TextEditor::GetText(const Coordinates & aStart, const Coordinates & aEnd) const
+{
+	std::string result;
+
+	auto lstart = aStart.mLine;
+	auto lend = aEnd.mLine;
+	auto istart = GetCharacterIndex(aStart);
+	auto iend = GetCharacterIndex(aEnd);
+	size_t s = 0;
+
+	for (size_t i = lstart; i < lend; i++)
+		s += mLines[i].size();
+
+	result.reserve(s + s / 8);
+
+	while (istart < iend || lstart < lend)
+	{
+		if (lstart >= (int)mLines.size())
+			break;
+
+		auto& line = mLines[lstart];
+		if (istart < (int)line.size())
+		{
+			result += line[istart].mChar;
+			istart++;
+		}
+		else
+		{
+			istart = 0;
+			++lstart;
+			result += '\n';
+		}
+	}
+
+	return result;
+}
+
+TextEditor::Coordinates TextEditor::GetActualCursorCoordinates() const
+{
+	return SanitizeCoordinates(mState.mCursorPosition);
+}
+
+TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates & aValue) const
+{
+	auto line = aValue.mLine;
+	auto column = aValue.mColumn;
+	if (line >= (int)mLines.size())
+	{
+		if (mLines.empty())
+		{
+			line = 0;
+			column = 0;
+		}
+		else
+		{
+			line = (int)mLines.size() - 1;
+			column = GetLineMaxColumn(line);
+		}
+		return Coordinates(line, column);
+	}
+	else
+	{
+		column = mLines.empty() ? 0 : std::min(column, GetLineMaxColumn(line));
+		return Coordinates(line, column);
+	}
+}
+
+// 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)
+static int UTF8CharLength(TextEditor::Char c)
+{
+	if ((c & 0xFE) == 0xFC)
+		return 6;
+	if ((c & 0xFC) == 0xF8)
+		return 5;
+	if ((c & 0xF8) == 0xF0)
+		return 4;
+	else if ((c & 0xF0) == 0xE0)
+		return 3;
+	else if ((c & 0xE0) == 0xC0)
+		return 2;
+	return 1;
+}
+
+// "Borrowed" from ImGui source
+static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
+{
+	if (c < 0x80)
+	{
+		buf[0] = (char)c;
+		return 1;
+	}
+	if (c < 0x800)
+	{
+		if (buf_size < 2) return 0;
+		buf[0] = (char)(0xc0 + (c >> 6));
+		buf[1] = (char)(0x80 + (c & 0x3f));
+		return 2;
+	}
+	if (c >= 0xdc00 && c < 0xe000)
+	{
+		return 0;
+	}
+	if (c >= 0xd800 && c < 0xdc00)
+	{
+		if (buf_size < 4) return 0;
+		buf[0] = (char)(0xf0 + (c >> 18));
+		buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
+		buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
+		buf[3] = (char)(0x80 + ((c) & 0x3f));
+		return 4;
+	}
+	//else if (c < 0x10000)
+	{
+		if (buf_size < 3) return 0;
+		buf[0] = (char)(0xe0 + (c >> 12));
+		buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
+		buf[2] = (char)(0x80 + ((c) & 0x3f));
+		return 3;
+	}
+}
+
+void TextEditor::Advance(Coordinates & aCoordinates) const
+{
+	if (aCoordinates.mLine < (int)mLines.size())
+	{
+		auto& line = mLines[aCoordinates.mLine];
+		auto cindex = GetCharacterIndex(aCoordinates);
+
+		if (cindex + 1 < (int)line.size())
+		{
+			auto delta = UTF8CharLength(line[cindex].mChar);
+			cindex = std::min(cindex + delta, (int)line.size() - 1);
+		}
+		else
+		{
+			++aCoordinates.mLine;
+			cindex = 0;
+		}
+		aCoordinates.mColumn = GetCharacterColumn(aCoordinates.mLine, cindex);
+	}
+}
+
+void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEnd)
+{
+	assert(aEnd >= aStart);
+	assert(!mReadOnly);
+
+	//printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine, aEnd.mColumn);
+
+	if (aEnd == aStart)
+		return;
+
+	auto start = GetCharacterIndex(aStart);
+	auto end = GetCharacterIndex(aEnd);
+
+	if (aStart.mLine == aEnd.mLine)
+	{
+		auto& line = mLines[aStart.mLine];
+		auto n = GetLineMaxColumn(aStart.mLine);
+		if (aEnd.mColumn >= n)
+			line.erase(line.begin() + start, line.end());
+		else
+			line.erase(line.begin() + start, line.begin() + end);
+	}
+	else
+	{
+		auto& firstLine = mLines[aStart.mLine];
+		auto& lastLine = mLines[aEnd.mLine];
+
+		firstLine.erase(firstLine.begin() + start, firstLine.end());
+		lastLine.erase(lastLine.begin(), lastLine.begin() + end);
+
+		if (aStart.mLine < aEnd.mLine)
+			firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end());
+
+		if (aStart.mLine < aEnd.mLine)
+			RemoveLine(aStart.mLine + 1, aEnd.mLine + 1);
+	}
+
+	mTextChanged = true;
+}
+
+int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValue)
+{
+	assert(!mReadOnly);
+
+	int cindex = GetCharacterIndex(aWhere);
+	int totalLines = 0;
+	while (*aValue != '\0')
+	{
+		assert(!mLines.empty());
+
+		if (*aValue == '\r')
+		{
+			// skip
+			++aValue;
+		}
+		else if (*aValue == '\n')
+		{
+			if (cindex < (int)mLines[aWhere.mLine].size())
+			{
+				auto& newLine = InsertLine(aWhere.mLine + 1);
+				auto& line = mLines[aWhere.mLine];
+				newLine.insert(newLine.begin(), line.begin() + cindex, line.end());
+				line.erase(line.begin() + cindex, line.end());
+			}
+			else
+			{
+				InsertLine(aWhere.mLine + 1);
+			}
+			++aWhere.mLine;
+			aWhere.mColumn = 0;
+			cindex = 0;
+			++totalLines;
+			++aValue;
+		}
+		else
+		{
+			auto& line = mLines[aWhere.mLine];
+			auto d = UTF8CharLength(*aValue);
+			while (d-- > 0 && *aValue != '\0')
+				line.insert(line.begin() + cindex++, Glyph(*aValue++, PaletteIndex::Default));
+			++aWhere.mColumn;
+		}
+
+		mTextChanged = true;
+	}
+
+	return totalLines;
+}
+
+void TextEditor::AddUndo(UndoRecord& aValue)
+{
+	assert(!mReadOnly);
+	//printf("AddUndo: (@%d.%d) +\'%s' [%d.%d .. %d.%d], -\'%s', [%d.%d .. %d.%d] (@%d.%d)\n",
+	//	aValue.mBefore.mCursorPosition.mLine, aValue.mBefore.mCursorPosition.mColumn,
+	//	aValue.mAdded.c_str(), aValue.mAddedStart.mLine, aValue.mAddedStart.mColumn, aValue.mAddedEnd.mLine, aValue.mAddedEnd.mColumn,
+	//	aValue.mRemoved.c_str(), aValue.mRemovedStart.mLine, aValue.mRemovedStart.mColumn, aValue.mRemovedEnd.mLine, aValue.mRemovedEnd.mColumn,
+	//	aValue.mAfter.mCursorPosition.mLine, aValue.mAfter.mCursorPosition.mColumn
+	//	);
+
+	mUndoBuffer.resize((size_t)(mUndoIndex + 1));
+	mUndoBuffer.back() = aValue;
+	++mUndoIndex;
+}
+
+TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPosition) const
+{
+	ImVec2 origin = ImGui::GetCursorScreenPos();
+	ImVec2 local(aPosition.x - origin.x, aPosition.y - origin.y);
+
+	int lineNo = std::max(0, (int)floor(local.y / mCharAdvance.y));
+
+	int columnCoord = 0;
+
+	if (lineNo >= 0 && lineNo < (int)mLines.size())
+	{
+		auto& line = mLines.at(lineNo);
+
+		int columnIndex = 0;
+		std::string cumulatedString = "";
+		float columnWidth = 0.0f;
+		float columnX = 0.0f;
+
+		// First we find the hovered column coord.
+		while (mTextStart + columnX < local.x && (size_t)columnIndex < line.size())
+		{
+			if (line[columnIndex].mChar == '\t')
+			{
+				float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ").x;
+				float oldX = columnX;
+				columnX = (1.0f + std::floor((1.0f + columnX) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);
+				columnWidth = columnX - oldX;
+				columnCoord++;
+			}
+			else
+			{
+				char buf[7];
+				auto d = UTF8CharLength(line[columnIndex].mChar);
+				int i = 0;
+				while (i < 6 && d-- > 0)
+					buf[i++] = line[columnIndex++].mChar;
+				buf[i] = '\0';
+				columnWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x;
+				columnX += columnWidth;
+				columnCoord++;
+			}
+		}
+
+		// Then we reduce by 1 column coord if cursor is on the left side of the hovered column.
+		if (mTextStart + columnX - columnWidth / 2.0f > local.x)
+			columnIndex = std::max(0, columnIndex - 1);
+	}
+
+	return SanitizeCoordinates(Coordinates(lineNo, columnCoord));
+}
+
+TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates & aFrom) const
+{
+	Coordinates at = aFrom;
+	if (at.mLine >= (int)mLines.size())
+		return at;
+
+	auto& line = mLines[at.mLine];
+	auto cindex = GetCharacterIndex(at);
+
+	if (cindex >= (int)line.size())
+		return at;
+
+	while (cindex > 0 && isspace(line[cindex].mChar))
+		--cindex;
+
+	auto cstart = (PaletteIndex)line[cindex].mColorIndex;
+	while (cindex > 0)
+	{
+		auto c = line[cindex].mChar;
+		if ((c & 0xC0) != 0x80)	// not UTF code sequence 10xxxxxx
+		{
+			if (c <= 32 && isspace(c))
+			{
+				cindex++;
+				break;
+			}
+			if (cstart != (PaletteIndex)line[size_t(cindex - 1)].mColorIndex)
+				break;
+		}
+		--cindex;
+	}
+	return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex));
+}
+
+TextEditor::Coordinates TextEditor::FindWordEnd(const Coordinates & aFrom) const
+{
+	Coordinates at = aFrom;
+	if (at.mLine >= (int)mLines.size())
+		return at;
+
+	auto& line = mLines[at.mLine];
+	auto cindex = GetCharacterIndex(at);
+
+	if (cindex >= (int)line.size())
+		return at;
+
+	bool prevspace = (bool)isspace(line[cindex].mChar);
+	auto cstart = (PaletteIndex)line[cindex].mColorIndex;
+	while (cindex < (int)line.size())
+	{
+		auto c = line[cindex].mChar;
+		auto d = UTF8CharLength(c);
+		if (cstart != (PaletteIndex)line[cindex].mColorIndex)
+			break;
+
+		if (prevspace != !!isspace(c))
+		{
+			if (isspace(c))
+				while (cindex < (int)line.size() && isspace(line[cindex].mChar))
+					++cindex;
+			break;
+		}
+		cindex += d;
+	}
+	return Coordinates(aFrom.mLine, GetCharacterColumn(aFrom.mLine, cindex));
+}
+
+TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates & aFrom) const
+{
+	Coordinates at = aFrom;
+	if (at.mLine >= (int)mLines.size())
+		return at;
+
+	// skip to the next non-word character
+	auto cindex = GetCharacterIndex(aFrom);
+	bool isword = false;
+	bool skip = false;
+	if (cindex < (int)mLines[at.mLine].size())
+	{
+		auto& line = mLines[at.mLine];
+		isword = isalnum(line[cindex].mChar);
+		skip = isword;
+	}
+
+	while (!isword || skip)
+	{
+		if (at.mLine >= mLines.size())
+		{
+			auto l = std::max(0, (int) mLines.size() - 1);
+			return Coordinates(l, GetLineMaxColumn(l));
+		}
+
+		auto& line = mLines[at.mLine];
+		if (cindex < (int)line.size())
+		{
+			isword = isalnum(line[cindex].mChar);
+
+			if (isword && !skip)
+				return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex));
+
+			if (!isword)
+				skip = false;
+
+			cindex++;
+		}
+		else
+		{
+			cindex = 0;
+			++at.mLine;
+			skip = false;
+			isword = false;
+		}
+	}
+
+	return at;
+}
+
+int TextEditor::GetCharacterIndex(const Coordinates& aCoordinates) const
+{
+	if (aCoordinates.mLine >= mLines.size())
+		return -1;
+	auto& line = mLines[aCoordinates.mLine];
+	int c = 0;
+	int i = 0;
+	for (; i < line.size() && c < aCoordinates.mColumn;)
+	{
+		if (line[i].mChar == '\t')
+			c = (c / mTabSize) * mTabSize + mTabSize;
+		else
+			++c;
+		i += UTF8CharLength(line[i].mChar);
+	}
+	return i;
+}
+
+int TextEditor::GetCharacterColumn(int aLine, int aIndex) const
+{
+	if (aLine >= mLines.size())
+		return 0;
+	auto& line = mLines[aLine];
+	int col = 0;
+	int i = 0;
+	while (i < aIndex && i < (int)line.size())
+	{
+		auto c = line[i].mChar;
+		i += UTF8CharLength(c);
+		if (c == '\t')
+			col = (col / mTabSize) * mTabSize + mTabSize;
+		else
+			col++;
+	}
+	return col;
+}
+
+int TextEditor::GetLineCharacterCount(int aLine) const
+{
+	if (aLine >= mLines.size())
+		return 0;
+	auto& line = mLines[aLine];
+	int c = 0;
+	for (unsigned i = 0; i < line.size(); c++)
+		i += UTF8CharLength(line[i].mChar);
+	return c;
+}
+
+int TextEditor::GetLineMaxColumn(int aLine) const
+{
+	if (aLine >= mLines.size())
+		return 0;
+	auto& line = mLines[aLine];
+	int col = 0;
+	for (unsigned i = 0; i < line.size(); )
+	{
+		auto c = line[i].mChar;
+		if (c == '\t')
+			col = (col / mTabSize) * mTabSize + mTabSize;
+		else
+			col++;
+		i += UTF8CharLength(c);
+	}
+	return col;
+}
+
+bool TextEditor::IsOnWordBoundary(const Coordinates & aAt) const
+{
+	if (aAt.mLine >= (int)mLines.size() || aAt.mColumn == 0)
+		return true;
+
+	auto& line = mLines[aAt.mLine];
+	auto cindex = GetCharacterIndex(aAt);
+	if (cindex >= (int)line.size())
+		return true;
+
+	if (mColorizerEnabled)
+		return line[cindex].mColorIndex != line[size_t(cindex - 1)].mColorIndex;
+
+	return isspace(line[cindex].mChar) != isspace(line[cindex - 1].mChar);
+}
+
+void TextEditor::RemoveLine(int aStart, int aEnd)
+{
+	assert(!mReadOnly);
+	assert(aEnd >= aStart);
+	assert(mLines.size() > (size_t)(aEnd - aStart));
+
+	ErrorMarkers etmp;
+	for (auto& i : mErrorMarkers)
+	{
+		ErrorMarkers::value_type e(i.first >= aStart ? i.first - 1 : i.first, i.second);
+		if (e.first >= aStart && e.first <= aEnd)
+			continue;
+		etmp.insert(e);
+	}
+	mErrorMarkers = std::move(etmp);
+
+	Breakpoints btmp;
+	for (auto i : mBreakpoints)
+	{
+		if (i >= aStart && i <= aEnd)
+			continue;
+		btmp.insert(i >= aStart ? i - 1 : i);
+	}
+	mBreakpoints = std::move(btmp);
+
+	mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd);
+	assert(!mLines.empty());
+
+	mTextChanged = true;
+}
+
+void TextEditor::RemoveLine(int aIndex)
+{
+	assert(!mReadOnly);
+	assert(mLines.size() > 1);
+
+	ErrorMarkers etmp;
+	for (auto& i : mErrorMarkers)
+	{
+		ErrorMarkers::value_type e(i.first > aIndex ? i.first - 1 : i.first, i.second);
+		if (e.first - 1 == aIndex)
+			continue;
+		etmp.insert(e);
+	}
+	mErrorMarkers = std::move(etmp);
+
+	Breakpoints btmp;
+	for (auto i : mBreakpoints)
+	{
+		if (i == aIndex)
+			continue;
+		btmp.insert(i >= aIndex ? i - 1 : i);
+	}
+	mBreakpoints = std::move(btmp);
+
+	mLines.erase(mLines.begin() + aIndex);
+	assert(!mLines.empty());
+
+	mTextChanged = true;
+}
+
+TextEditor::Line& TextEditor::InsertLine(int aIndex)
+{
+	assert(!mReadOnly);
+
+	auto& result = *mLines.insert(mLines.begin() + aIndex, Line());
+
+	ErrorMarkers etmp;
+	for (auto& i : mErrorMarkers)
+		etmp.insert(ErrorMarkers::value_type(i.first >= aIndex ? i.first + 1 : i.first, i.second));
+	mErrorMarkers = std::move(etmp);
+
+	Breakpoints btmp;
+	for (auto i : mBreakpoints)
+		btmp.insert(i >= aIndex ? i + 1 : i);
+	mBreakpoints = std::move(btmp);
+
+	return result;
+}
+
+std::string TextEditor::GetWordUnderCursor() const
+{
+	auto c = GetCursorPosition();
+	return GetWordAt(c);
+}
+
+std::string TextEditor::GetWordAt(const Coordinates & aCoords) const
+{
+	auto start = FindWordStart(aCoords);
+	auto end = FindWordEnd(aCoords);
+
+	std::string r;
+
+	auto istart = GetCharacterIndex(start);
+	auto iend = GetCharacterIndex(end);
+
+	for (auto it = istart; it < iend; ++it)
+		r.push_back(mLines[aCoords.mLine][it].mChar);
+
+	return r;
+}
+
+ImU32 TextEditor::GetGlyphColor(const Glyph & aGlyph) const
+{
+	if (!mColorizerEnabled)
+		return mPalette[(int)PaletteIndex::Default];
+	if (aGlyph.mComment)
+		return mPalette[(int)PaletteIndex::Comment];
+	if (aGlyph.mMultiLineComment)
+		return mPalette[(int)PaletteIndex::MultiLineComment];
+	auto const color = mPalette[(int)aGlyph.mColorIndex];
+	if (aGlyph.mPreprocessor)
+	{
+		const auto ppcolor = mPalette[(int)PaletteIndex::Preprocessor];
+		const int c0 = ((ppcolor & 0xff) + (color & 0xff)) / 2;
+		const int c1 = (((ppcolor >> 8) & 0xff) + ((color >> 8) & 0xff)) / 2;
+		const int c2 = (((ppcolor >> 16) & 0xff) + ((color >> 16) & 0xff)) / 2;
+		const int c3 = (((ppcolor >> 24) & 0xff) + ((color >> 24) & 0xff)) / 2;
+		return ImU32(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24));
+	}
+	return color;
+}
+
+void TextEditor::HandleKeyboardInputs()
+{
+	ImGuiIO& io = ImGui::GetIO();
+	auto shift = io.KeyShift;
+	auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
+	auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
+
+	if (ImGui::IsWindowFocused())
+	{
+		if (ImGui::IsWindowHovered())
+			ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput);
+		//ImGui::CaptureKeyboardFromApp(true);
+
+		io.WantCaptureKeyboard = true;
+		io.WantTextInput = true;
+
+		if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Z)))
+			Undo();
+		else if (!IsReadOnly() && !ctrl && !shift && alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace)))
+			Undo();
+		else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y)))
+			Redo();
+		else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
+			MoveUp(1, shift);
+		else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))
+			MoveDown(1, shift);
+		else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)))
+			MoveLeft(1, shift, ctrl);
+		else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)))
+			MoveRight(1, shift, ctrl);
+		else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp)))
+			MoveUp(GetPageSize() - 4, shift);
+		else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown)))
+			MoveDown(GetPageSize() - 4, shift);
+		else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
+			MoveTop(shift);
+		else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
+			MoveBottom(shift);
+		else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
+			MoveHome(shift);
+		else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
+			MoveEnd(shift);
+		else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))
+			Delete();
+		else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace)))
+			Backspace();
+		else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
+			mOverwrite ^= true;
+		else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
+			Copy();
+		else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))
+			Copy();
+		else if (!IsReadOnly() && !ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
+			Paste();
+		else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_V)))
+			Paste();
+		else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_X)))
+			Cut();
+		else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))
+			Cut();
+		else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_A)))
+			SelectAll();
+		else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter)))
+			EnterCharacter('\n', false);
+		else if (!IsReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Tab)))
+			EnterCharacter('\t', shift);
+		if (!IsReadOnly() && !io.InputQueueCharacters.empty() && !ctrl)
+		{
+			for (int 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.KeySuper : io.KeyCtrl;
+	auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
+
+	if (ImGui::IsWindowHovered())
+	{
+		if (!shift && !alt)
+		{
+			auto click = ImGui::IsMouseClicked(0);
+			auto doubleClick = ImGui::IsMouseDoubleClicked(0);
+			auto t = ImGui::GetTime();
+			auto tripleClick = click && !doubleClick && (mLastClick != -1.0f && (t - mLastClick) < io.MouseDoubleClickTime);
+
+			/*
+			Left mouse button triple click
+			*/
+
+			if (tripleClick)
+			{
+				if (!ctrl)
+				{
+					mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos()));
+					mSelectionMode = SelectionMode::Line;
+					SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);
+				}
+
+				mLastClick = -1.0f;
+			}
+
+			/*
+			Left mouse button double click
+			*/
+
+			else if (doubleClick)
+			{
+				if (!ctrl)
+				{
+					mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos()));
+					if (mSelectionMode == SelectionMode::Line)
+						mSelectionMode = SelectionMode::Normal;
+					else
+						mSelectionMode = SelectionMode::Word;
+					SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);
+				}
+
+				mLastClick = (float)ImGui::GetTime();
+			}
+
+			/*
+			Left mouse button click
+			*/
+			else if (click)
+			{
+				mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos()));
+				if (ctrl)
+					mSelectionMode = SelectionMode::Word;
+				else
+					mSelectionMode = SelectionMode::Normal;
+				SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);
+
+				mLastClick = (float)ImGui::GetTime();
+			}
+			// Mouse left button dragging (=> update selection)
+			else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0))
+			{
+				io.WantCaptureMouse = true;
+				mState.mCursorPosition = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos()));
+				SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);
+			}
+		}
+	}
+}
+
+void TextEditor::Render()
+{
+	/* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/
+	const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x;
+	mCharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing);
+
+	/* Update palette with the current alpha from style */
+	for (int i = 0; i < (int)PaletteIndex::Max; ++i)
+	{
+		auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]);
+		color.w *= ImGui::GetStyle().Alpha;
+		mPalette[i] = ImGui::ColorConvertFloat4ToU32(color);
+	}
+
+	assert(mLineBuffer.empty());
+
+	auto contentSize = ImGui::GetWindowContentRegionMax();
+	auto drawList = ImGui::GetWindowDrawList();
+	float longest(mTextStart);
+
+	if (mScrollToTop)
+	{
+		mScrollToTop = false;
+		ImGui::SetScrollY(0.f);
+	}
+
+	ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos();
+	auto scrollX = ImGui::GetScrollX();
+	auto scrollY = ImGui::GetScrollY();
+
+	auto lineNo = (int)floor(scrollY / mCharAdvance.y);
+	auto globalLineMax = (int)mLines.size();
+	auto lineMax = std::max(0, std::min((int)mLines.size() - 1, lineNo + (int)floor((scrollY + contentSize.y) / mCharAdvance.y)));
+
+	// Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width
+	char buf[16];
+	snprintf(buf, 16, " %d ", globalLineMax);
+	mTextStart = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x + mLeftMargin;
+
+	if (!mLines.empty())
+	{
+		float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x;
+
+		while (lineNo <= lineMax)
+		{
+			ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * mCharAdvance.y);
+			ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + mTextStart, lineStartScreenPos.y);
+
+			auto& line = mLines[lineNo];
+			longest = std::max(mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest);
+			auto columnNo = 0;
+			Coordinates lineStartCoord(lineNo, 0);
+			Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo));
+
+			// Draw selection for the current line
+			float sstart = -1.0f;
+			float ssend = -1.0f;
+
+			assert(mState.mSelectionStart <= mState.mSelectionEnd);
+			if (mState.mSelectionStart <= lineEndCoord)
+				sstart = mState.mSelectionStart > lineStartCoord ? TextDistanceToLineStart(mState.mSelectionStart) : 0.0f;
+			if (mState.mSelectionEnd > lineStartCoord)
+				ssend = TextDistanceToLineStart(mState.mSelectionEnd < lineEndCoord ? mState.mSelectionEnd : lineEndCoord);
+
+			if (mState.mSelectionEnd.mLine > lineNo)
+				ssend += mCharAdvance.x;
+
+			if (sstart != -1 && ssend != -1 && sstart < ssend)
+			{
+				ImVec2 vstart(lineStartScreenPos.x + mTextStart + sstart, lineStartScreenPos.y);
+				ImVec2 vend(lineStartScreenPos.x + mTextStart + ssend, lineStartScreenPos.y + mCharAdvance.y);
+				drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]);
+			}
+
+			// Draw breakpoints
+			auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y);
+
+			if (mBreakpoints.count(lineNo + 1) != 0)
+			{
+				auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y);
+				drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]);
+			}
+
+			// Draw error markers
+			auto errorIt = mErrorMarkers.find(lineNo + 1);
+			if (errorIt != mErrorMarkers.end())
+			{
+				auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y);
+				drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::ErrorMarker]);
+
+				if (ImGui::IsMouseHoveringRect(lineStartScreenPos, end))
+				{
+					ImGui::BeginTooltip();
+					ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f));
+					ImGui::Text("Error at line %d:", errorIt->first);
+					ImGui::PopStyleColor();
+					ImGui::Separator();
+					ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.2f, 1.0f));
+					ImGui::Text("%s", errorIt->second.c_str());
+					ImGui::PopStyleColor();
+					ImGui::EndTooltip();
+				}
+			}
+
+			// Draw line number (right aligned)
+			snprintf(buf, 16, "%d  ", lineNo + 1);
+
+			auto lineNoWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x;
+			drawList->AddText(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y), mPalette[(int)PaletteIndex::LineNumber], buf);
+
+			if (mState.mCursorPosition.mLine == lineNo)
+			{
+				auto focused = ImGui::IsWindowFocused();
+
+				// Highlight the current line (where the cursor is)
+				if (!HasSelection())
+				{
+					auto end = ImVec2(start.x + contentSize.x + scrollX, start.y + mCharAdvance.y);
+					drawList->AddRectFilled(start, end, mPalette[(int)(focused ? PaletteIndex::CurrentLineFill : PaletteIndex::CurrentLineFillInactive)]);
+					drawList->AddRect(start, end, mPalette[(int)PaletteIndex::CurrentLineEdge], 1.0f);
+				}
+
+				// Render the cursor
+				if (focused)
+				{
+					auto timeEnd = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
+					auto elapsed = timeEnd - mStartTime;
+					if (elapsed > 400)
+					{
+						float width = 1.0f;
+						auto cindex = GetCharacterIndex(mState.mCursorPosition);
+						float cx = TextDistanceToLineStart(mState.mCursorPosition);
+
+						if (mOverwrite && cindex < (int)line.size())
+						{
+							auto c = line[cindex].mChar;
+							if (c == '\t')
+							{
+								auto x = (1.0f + std::floor((1.0f + cx) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);
+								width = x - cx;
+							}
+							else
+							{
+								char buf[2];
+								buf[0] = line[cindex].mChar;
+								buf[1] = '\0';
+								width = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x;
+							}
+						}
+						ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y);
+						ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + mCharAdvance.y);
+						drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]);
+						if (elapsed > 800)
+							mStartTime = timeEnd;
+					}
+				}
+			}
+
+			// Render colorized text
+			auto prevColor = line.empty() ? mPalette[(int)PaletteIndex::Default] : GetGlyphColor(line[0]);
+			ImVec2 bufferOffset;
+
+			for (int i = 0; i < line.size();)
+			{
+				auto& glyph = line[i];
+				auto color = GetGlyphColor(glyph);
+
+				if ((color != prevColor || glyph.mChar == '\t' || glyph.mChar == ' ') && !mLineBuffer.empty())
+				{
+					const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y);
+					drawList->AddText(newOffset, prevColor, mLineBuffer.c_str());
+					auto textSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, mLineBuffer.c_str(), nullptr, nullptr);
+					bufferOffset.x += textSize.x;
+					mLineBuffer.clear();
+				}
+				prevColor = color;
+
+				if (glyph.mChar == '\t')
+				{
+					auto oldX = bufferOffset.x;
+					bufferOffset.x = (1.0f + std::floor((1.0f + bufferOffset.x) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);
+					++i;
+
+					if (mShowWhitespaces)
+					{
+						const auto s = ImGui::GetFontSize();
+						const auto x1 = textScreenPos.x + oldX + 1.0f;
+						const auto x2 = textScreenPos.x + bufferOffset.x - 1.0f;
+						const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f;
+						const ImVec2 p1(x1, y);
+						const ImVec2 p2(x2, y);
+						const ImVec2 p3(x2 - s * 0.2f, y - s * 0.2f);
+						const ImVec2 p4(x2 - s * 0.2f, y + s * 0.2f);
+						drawList->AddLine(p1, p2, 0x90909090);
+						drawList->AddLine(p2, p3, 0x90909090);
+						drawList->AddLine(p2, p4, 0x90909090);
+					}
+				}
+				else if (glyph.mChar == ' ')
+				{
+					if (mShowWhitespaces)
+					{
+						const auto s = ImGui::GetFontSize();
+						const auto x = textScreenPos.x + bufferOffset.x + spaceSize * 0.5f;
+						const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f;
+						drawList->AddCircleFilled(ImVec2(x, y), 1.5f, 0x80808080, 4);
+					}
+					bufferOffset.x += spaceSize;
+					i++;
+				}
+				else
+				{
+					auto l = UTF8CharLength(glyph.mChar);
+					while (l-- > 0)
+						mLineBuffer.push_back(line[i++].mChar);
+				}
+				++columnNo;
+			}
+
+			if (!mLineBuffer.empty())
+			{
+				const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y);
+				drawList->AddText(newOffset, prevColor, mLineBuffer.c_str());
+				mLineBuffer.clear();
+			}
+
+			++lineNo;
+		}
+
+		// Draw a tooltip on known identifiers/preprocessor symbols
+		if (ImGui::IsMousePosValid())
+		{
+			auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos()));
+			if (!id.empty())
+			{
+				auto it = mLanguageDefinition.mIdentifiers.find(id);
+				if (it != mLanguageDefinition.mIdentifiers.end())
+				{
+					ImGui::BeginTooltip();
+					ImGui::TextUnformatted(it->second.mDeclaration.c_str());
+					ImGui::EndTooltip();
+				}
+				else
+				{
+					auto pi = mLanguageDefinition.mPreprocIdentifiers.find(id);
+					if (pi != mLanguageDefinition.mPreprocIdentifiers.end())
+					{
+						ImGui::BeginTooltip();
+						ImGui::TextUnformatted(pi->second.mDeclaration.c_str());
+						ImGui::EndTooltip();
+					}
+				}
+			}
+		}
+	}
+
+
+	ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y));
+
+	if (mScrollToCursor)
+	{
+		EnsureCursorVisible();
+		ImGui::SetWindowFocus();
+		mScrollToCursor = false;
+	}
+}
+
+void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
+{
+	mWithinRender = true;
+	mTextChanged = false;
+	mCursorPositionChanged = false;
+
+	ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background]));
+	ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
+	if (!mIgnoreImGuiChild)
+		ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove);
+
+	if (mHandleKeyboardInputs)
+	{
+		HandleKeyboardInputs();
+		ImGui::PushAllowKeyboardFocus(true);
+	}
+
+	if (mHandleMouseInputs)
+		HandleMouseInputs();
+
+	ColorizeInternal();
+	Render();
+
+	if (mHandleKeyboardInputs)
+		ImGui::PopAllowKeyboardFocus();
+
+	if (!mIgnoreImGuiChild)
+		ImGui::EndChild();
+
+	ImGui::PopStyleVar();
+	ImGui::PopStyleColor();
+
+	mWithinRender = false;
+}
+
+void TextEditor::SetText(const std::string & aText)
+{
+	mLines.clear();
+	mLines.emplace_back(Line());
+	for (auto chr : aText)
+	{
+		if (chr == '\r')
+		{
+			// ignore the carriage return character
+		}
+		else if (chr == '\n')
+			mLines.emplace_back(Line());
+		else
+		{
+			mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default));
+		}
+	}
+
+	mTextChanged = true;
+	mScrollToTop = true;
+
+	mUndoBuffer.clear();
+	mUndoIndex = 0;
+
+	Colorize();
+}
+
+void TextEditor::SetTextLines(const std::vector<std::string> & aLines)
+{
+	mLines.clear();
+
+	if (aLines.empty())
+	{
+		mLines.emplace_back(Line());
+	}
+	else
+	{
+		mLines.resize(aLines.size());
+
+		for (size_t i = 0; i < aLines.size(); ++i)
+		{
+			const std::string & aLine = aLines[i];
+
+			mLines[i].reserve(aLine.size());
+			for (size_t j = 0; j < aLine.size(); ++j)
+				mLines[i].emplace_back(Glyph(aLine[j], PaletteIndex::Default));
+		}
+	}
+
+	mTextChanged = true;
+	mScrollToTop = true;
+
+	mUndoBuffer.clear();
+	mUndoIndex = 0;
+
+	Colorize();
+}
+
+void TextEditor::EnterCharacter(ImWchar aChar, bool aShift)
+{
+	assert(!mReadOnly);
+
+	UndoRecord u;
+
+	u.mBefore = mState;
+
+	if (HasSelection())
+	{
+		if (aChar == '\t' && mState.mSelectionStart.mLine != mState.mSelectionEnd.mLine)
+		{
+
+			auto start = mState.mSelectionStart;
+			auto end = mState.mSelectionEnd;
+			auto originalEnd = end;
+
+			if (start > end)
+				std::swap(start, end);
+			start.mColumn = 0;
+			//			end.mColumn = end.mLine < mLines.size() ? mLines[end.mLine].size() : 0;
+			if (end.mColumn == 0 && end.mLine > 0)
+				--end.mLine;
+			if (end.mLine >= (int)mLines.size())
+				end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1;
+			end.mColumn = GetLineMaxColumn(end.mLine);
+
+			//if (end.mColumn >= GetLineMaxColumn(end.mLine))
+			//	end.mColumn = GetLineMaxColumn(end.mLine) - 1;
+
+			u.mRemovedStart = start;
+			u.mRemovedEnd = end;
+			u.mRemoved = GetText(start, end);
+
+			bool modified = false;
+
+			for (int i = start.mLine; i <= end.mLine; i++)
+			{
+				auto& line = mLines[i];
+				if (aShift)
+				{
+					if (!line.empty())
+					{
+						if (line.front().mChar == '\t')
+						{
+							line.erase(line.begin());
+							modified = true;
+						}
+						else
+						{
+							for (int j = 0; j < mTabSize && !line.empty() && line.front().mChar == ' '; j++)
+							{
+								line.erase(line.begin());
+								modified = true;
+							}
+						}
+					}
+				}
+				else
+				{
+					line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background));
+					modified = true;
+				}
+			}
+
+			if (modified)
+			{
+				start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0));
+				Coordinates rangeEnd;
+				if (originalEnd.mColumn != 0)
+				{
+					end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine));
+					rangeEnd = end;
+					u.mAdded = GetText(start, end);
+				}
+				else
+				{
+					end = Coordinates(originalEnd.mLine, 0);
+					rangeEnd = Coordinates(end.mLine - 1, GetLineMaxColumn(end.mLine - 1));
+					u.mAdded = GetText(start, rangeEnd);
+				}
+
+				u.mAddedStart = start;
+				u.mAddedEnd = rangeEnd;
+				u.mAfter = mState;
+
+				mState.mSelectionStart = start;
+				mState.mSelectionEnd = end;
+				AddUndo(u);
+
+				mTextChanged = true;
+
+				EnsureCursorVisible();
+			}
+
+			return;
+		} // c == '\t'
+		else
+		{
+			u.mRemoved = GetSelectedText();
+			u.mRemovedStart = mState.mSelectionStart;
+			u.mRemovedEnd = mState.mSelectionEnd;
+			DeleteSelection();
+		}
+	} // HasSelection
+
+	auto coord = GetActualCursorCoordinates();
+	u.mAddedStart = coord;
+
+	assert(!mLines.empty());
+
+	if (aChar == '\n')
+	{
+		InsertLine(coord.mLine + 1);
+		auto& line = mLines[coord.mLine];
+		auto& newLine = mLines[coord.mLine + 1];
+
+		if (mLanguageDefinition.mAutoIndentation)
+			for (size_t it = 0; it < line.size() && isascii(line[it].mChar) && isblank(line[it].mChar); ++it)
+				newLine.push_back(line[it]);
+
+		const size_t whitespaceSize = newLine.size();
+		auto cindex = GetCharacterIndex(coord);
+		newLine.insert(newLine.end(), line.begin() + cindex, line.end());
+		line.erase(line.begin() + cindex, line.begin() + line.size());
+		SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize)));
+		u.mAdded = (char)aChar;
+	}
+	else
+	{
+		char buf[7];
+		int e = ImTextCharToUtf8(buf, 7, aChar);
+		if (e > 0)
+		{
+			buf[e] = '\0';
+			auto& line = mLines[coord.mLine];
+			auto cindex = GetCharacterIndex(coord);
+
+			if (mOverwrite && cindex < (int)line.size())
+			{
+				auto d = UTF8CharLength(line[cindex].mChar);
+
+				u.mRemovedStart = mState.mCursorPosition;
+				u.mRemovedEnd = Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + d));
+
+				while (d-- > 0 && cindex < (int)line.size())
+				{
+					u.mRemoved += line[cindex].mChar;
+					line.erase(line.begin() + cindex);
+				}
+			}
+
+			for (auto p = buf; *p != '\0'; p++, ++cindex)
+				line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default));
+			u.mAdded = buf;
+
+			SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex)));
+		}
+		else
+			return;
+	}
+
+	mTextChanged = true;
+
+	u.mAddedEnd = GetActualCursorCoordinates();
+	u.mAfter = mState;
+
+	AddUndo(u);
+
+	Colorize(coord.mLine - 1, 3);
+	EnsureCursorVisible();
+}
+
+void TextEditor::SetReadOnly(bool aValue)
+{
+	mReadOnly = aValue;
+}
+
+void TextEditor::SetColorizerEnable(bool aValue)
+{
+	mColorizerEnabled = aValue;
+}
+
+void TextEditor::SetCursorPosition(const Coordinates & aPosition)
+{
+	if (mState.mCursorPosition != aPosition)
+	{
+		mState.mCursorPosition = aPosition;
+		mCursorPositionChanged = true;
+		EnsureCursorVisible();
+	}
+}
+
+void TextEditor::SetSelectionStart(const Coordinates & aPosition)
+{
+	mState.mSelectionStart = SanitizeCoordinates(aPosition);
+	if (mState.mSelectionStart > mState.mSelectionEnd)
+		std::swap(mState.mSelectionStart, mState.mSelectionEnd);
+}
+
+void TextEditor::SetSelectionEnd(const Coordinates & aPosition)
+{
+	mState.mSelectionEnd = SanitizeCoordinates(aPosition);
+	if (mState.mSelectionStart > mState.mSelectionEnd)
+		std::swap(mState.mSelectionStart, mState.mSelectionEnd);
+}
+
+void TextEditor::SetSelection(const Coordinates & aStart, const Coordinates & aEnd, SelectionMode aMode)
+{
+	auto oldSelStart = mState.mSelectionStart;
+	auto oldSelEnd = mState.mSelectionEnd;
+
+	mState.mSelectionStart = SanitizeCoordinates(aStart);
+	mState.mSelectionEnd = SanitizeCoordinates(aEnd);
+	if (mState.mSelectionStart > mState.mSelectionEnd)
+		std::swap(mState.mSelectionStart, mState.mSelectionEnd);
+
+	switch (aMode)
+	{
+	case TextEditor::SelectionMode::Normal:
+		break;
+	case TextEditor::SelectionMode::Word:
+	{
+		mState.mSelectionStart = FindWordStart(mState.mSelectionStart);
+		if (!IsOnWordBoundary(mState.mSelectionEnd))
+			mState.mSelectionEnd = FindWordEnd(FindWordStart(mState.mSelectionEnd));
+		break;
+	}
+	case TextEditor::SelectionMode::Line:
+	{
+		const auto lineNo = mState.mSelectionEnd.mLine;
+		const auto lineSize = (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0;
+		mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0);
+		mState.mSelectionEnd = Coordinates(lineNo, GetLineMaxColumn(lineNo));
+		break;
+	}
+	default:
+		break;
+	}
+
+	if (mState.mSelectionStart != oldSelStart ||
+		mState.mSelectionEnd != oldSelEnd)
+		mCursorPositionChanged = true;
+}
+
+void TextEditor::SetTabSize(int aValue)
+{
+	mTabSize = std::max(0, std::min(32, aValue));
+}
+
+void TextEditor::InsertText(const std::string & aValue)
+{
+	InsertText(aValue.c_str());
+}
+
+void TextEditor::InsertText(const char * aValue)
+{
+	if (aValue == nullptr)
+		return;
+
+	auto pos = GetActualCursorCoordinates();
+	auto start = std::min(pos, mState.mSelectionStart);
+	int totalLines = pos.mLine - start.mLine;
+
+	totalLines += InsertTextAt(pos, aValue);
+
+	SetSelection(pos, pos);
+	SetCursorPosition(pos);
+	Colorize(start.mLine - 1, totalLines + 2);
+}
+
+void TextEditor::DeleteSelection()
+{
+	assert(mState.mSelectionEnd >= mState.mSelectionStart);
+
+	if (mState.mSelectionEnd == mState.mSelectionStart)
+		return;
+
+	DeleteRange(mState.mSelectionStart, mState.mSelectionEnd);
+
+	SetSelection(mState.mSelectionStart, mState.mSelectionStart);
+	SetCursorPosition(mState.mSelectionStart);
+	Colorize(mState.mSelectionStart.mLine, 1);
+}
+
+void TextEditor::MoveUp(int aAmount, bool aSelect)
+{
+	auto oldPos = mState.mCursorPosition;
+	mState.mCursorPosition.mLine = std::max(0, mState.mCursorPosition.mLine - aAmount);
+	if (oldPos != mState.mCursorPosition)
+	{
+		if (aSelect)
+		{
+			if (oldPos == mInteractiveStart)
+				mInteractiveStart = mState.mCursorPosition;
+			else if (oldPos == mInteractiveEnd)
+				mInteractiveEnd = mState.mCursorPosition;
+			else
+			{
+				mInteractiveStart = mState.mCursorPosition;
+				mInteractiveEnd = oldPos;
+			}
+		}
+		else
+			mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
+		SetSelection(mInteractiveStart, mInteractiveEnd);
+
+		EnsureCursorVisible();
+	}
+}
+
+void TextEditor::MoveDown(int aAmount, bool aSelect)
+{
+	assert(mState.mCursorPosition.mColumn >= 0);
+	auto oldPos = mState.mCursorPosition;
+	mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + aAmount));
+
+	if (mState.mCursorPosition != oldPos)
+	{
+		if (aSelect)
+		{
+			if (oldPos == mInteractiveEnd)
+				mInteractiveEnd = mState.mCursorPosition;
+			else if (oldPos == mInteractiveStart)
+				mInteractiveStart = mState.mCursorPosition;
+			else
+			{
+				mInteractiveStart = oldPos;
+				mInteractiveEnd = mState.mCursorPosition;
+			}
+		}
+		else
+			mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
+		SetSelection(mInteractiveStart, mInteractiveEnd);
+
+		EnsureCursorVisible();
+	}
+}
+
+static bool IsUTFSequence(char c)
+{
+	return (c & 0xC0) == 0x80;
+}
+
+void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode)
+{
+	if (mLines.empty())
+		return;
+
+	auto oldPos = mState.mCursorPosition;
+	mState.mCursorPosition = GetActualCursorCoordinates();
+	auto line = mState.mCursorPosition.mLine;
+	auto cindex = GetCharacterIndex(mState.mCursorPosition);
+
+	while (aAmount-- > 0)
+	{
+		if (cindex == 0)
+		{
+			if (line > 0)
+			{
+				--line;
+				if ((int)mLines.size() > line)
+					cindex = (int)mLines[line].size();
+				else
+					cindex = 0;
+			}
+		}
+		else
+		{
+			--cindex;
+			if (cindex > 0)
+			{
+				if ((int)mLines.size() > line)
+				{
+					while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar))
+						--cindex;
+				}
+			}
+		}
+
+		mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex));
+		if (aWordMode)
+		{
+			mState.mCursorPosition = FindWordStart(mState.mCursorPosition);
+			cindex = GetCharacterIndex(mState.mCursorPosition);
+		}
+	}
+
+	mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex));
+
+	assert(mState.mCursorPosition.mColumn >= 0);
+	if (aSelect)
+	{
+		if (oldPos == mInteractiveStart)
+			mInteractiveStart = mState.mCursorPosition;
+		else if (oldPos == mInteractiveEnd)
+			mInteractiveEnd = mState.mCursorPosition;
+		else
+		{
+			mInteractiveStart = mState.mCursorPosition;
+			mInteractiveEnd = oldPos;
+		}
+	}
+	else
+		mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
+	SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal);
+
+	EnsureCursorVisible();
+}
+
+void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode)
+{
+	auto oldPos = mState.mCursorPosition;
+
+	if (mLines.empty() || oldPos.mLine >= mLines.size())
+		return;
+
+	auto cindex = GetCharacterIndex(mState.mCursorPosition);
+	while (aAmount-- > 0)
+	{
+		auto lindex = mState.mCursorPosition.mLine;
+		auto& line = mLines[lindex];
+
+		if (cindex >= line.size())
+		{
+			if (mState.mCursorPosition.mLine < mLines.size() - 1)
+			{
+				mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + 1));
+				mState.mCursorPosition.mColumn = 0;
+			}
+			else
+				return;
+		}
+		else
+		{
+			cindex += UTF8CharLength(line[cindex].mChar);
+			mState.mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex));
+			if (aWordMode)
+				mState.mCursorPosition = FindNextWord(mState.mCursorPosition);
+		}
+	}
+
+	if (aSelect)
+	{
+		if (oldPos == mInteractiveEnd)
+			mInteractiveEnd = SanitizeCoordinates(mState.mCursorPosition);
+		else if (oldPos == mInteractiveStart)
+			mInteractiveStart = mState.mCursorPosition;
+		else
+		{
+			mInteractiveStart = oldPos;
+			mInteractiveEnd = mState.mCursorPosition;
+		}
+	}
+	else
+		mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
+	SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal);
+
+	EnsureCursorVisible();
+}
+
+void TextEditor::MoveTop(bool aSelect)
+{
+	auto oldPos = mState.mCursorPosition;
+	SetCursorPosition(Coordinates(0, 0));
+
+	if (mState.mCursorPosition != oldPos)
+	{
+		if (aSelect)
+		{
+			mInteractiveEnd = oldPos;
+			mInteractiveStart = mState.mCursorPosition;
+		}
+		else
+			mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
+		SetSelection(mInteractiveStart, mInteractiveEnd);
+	}
+}
+
+void TextEditor::TextEditor::MoveBottom(bool aSelect)
+{
+	auto oldPos = GetCursorPosition();
+	auto newPos = Coordinates((int)mLines.size() - 1, 0);
+	SetCursorPosition(newPos);
+	if (aSelect)
+	{
+		mInteractiveStart = oldPos;
+		mInteractiveEnd = newPos;
+	}
+	else
+		mInteractiveStart = mInteractiveEnd = newPos;
+	SetSelection(mInteractiveStart, mInteractiveEnd);
+}
+
+void TextEditor::MoveHome(bool aSelect)
+{
+	auto oldPos = mState.mCursorPosition;
+	SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, 0));
+
+	if (mState.mCursorPosition != oldPos)
+	{
+		if (aSelect)
+		{
+			if (oldPos == mInteractiveStart)
+				mInteractiveStart = mState.mCursorPosition;
+			else if (oldPos == mInteractiveEnd)
+				mInteractiveEnd = mState.mCursorPosition;
+			else
+			{
+				mInteractiveStart = mState.mCursorPosition;
+				mInteractiveEnd = oldPos;
+			}
+		}
+		else
+			mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
+		SetSelection(mInteractiveStart, mInteractiveEnd);
+	}
+}
+
+void TextEditor::MoveEnd(bool aSelect)
+{
+	auto oldPos = mState.mCursorPosition;
+	SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, GetLineMaxColumn(oldPos.mLine)));
+
+	if (mState.mCursorPosition != oldPos)
+	{
+		if (aSelect)
+		{
+			if (oldPos == mInteractiveEnd)
+				mInteractiveEnd = mState.mCursorPosition;
+			else if (oldPos == mInteractiveStart)
+				mInteractiveStart = mState.mCursorPosition;
+			else
+			{
+				mInteractiveStart = oldPos;
+				mInteractiveEnd = mState.mCursorPosition;
+			}
+		}
+		else
+			mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
+		SetSelection(mInteractiveStart, mInteractiveEnd);
+	}
+}
+
+void TextEditor::Delete()
+{
+	assert(!mReadOnly);
+
+	if (mLines.empty())
+		return;
+
+	UndoRecord u;
+	u.mBefore = mState;
+
+	if (HasSelection())
+	{
+		u.mRemoved = GetSelectedText();
+		u.mRemovedStart = mState.mSelectionStart;
+		u.mRemovedEnd = mState.mSelectionEnd;
+
+		DeleteSelection();
+	}
+	else
+	{
+		auto pos = GetActualCursorCoordinates();
+		SetCursorPosition(pos);
+		auto& line = mLines[pos.mLine];
+
+		if (pos.mColumn == GetLineMaxColumn(pos.mLine))
+		{
+			if (pos.mLine == (int)mLines.size() - 1)
+				return;
+
+			u.mRemoved = '\n';
+			u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates();
+			Advance(u.mRemovedEnd);
+
+			auto& nextLine = mLines[pos.mLine + 1];
+			line.insert(line.end(), nextLine.begin(), nextLine.end());
+			RemoveLine(pos.mLine + 1);
+		}
+		else
+		{
+			auto cindex = GetCharacterIndex(pos);
+			u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates();
+			u.mRemovedEnd.mColumn++;
+			u.mRemoved = GetText(u.mRemovedStart, u.mRemovedEnd);
+
+			auto d = UTF8CharLength(line[cindex].mChar);
+			while (d-- > 0 && cindex < (int)line.size())
+				line.erase(line.begin() + cindex);
+		}
+
+		mTextChanged = true;
+
+		Colorize(pos.mLine, 1);
+	}
+
+	u.mAfter = mState;
+	AddUndo(u);
+}
+
+void TextEditor::Backspace()
+{
+	assert(!mReadOnly);
+
+	if (mLines.empty())
+		return;
+
+	UndoRecord u;
+	u.mBefore = mState;
+
+	if (HasSelection())
+	{
+		u.mRemoved = GetSelectedText();
+		u.mRemovedStart = mState.mSelectionStart;
+		u.mRemovedEnd = mState.mSelectionEnd;
+
+		DeleteSelection();
+	}
+	else
+	{
+		auto pos = GetActualCursorCoordinates();
+		SetCursorPosition(pos);
+
+		if (mState.mCursorPosition.mColumn == 0)
+		{
+			if (mState.mCursorPosition.mLine == 0)
+				return;
+
+			u.mRemoved = '\n';
+			u.mRemovedStart = u.mRemovedEnd = Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1));
+			Advance(u.mRemovedEnd);
+
+			auto& line = mLines[mState.mCursorPosition.mLine];
+			auto& prevLine = mLines[mState.mCursorPosition.mLine - 1];
+			auto prevSize = GetLineMaxColumn(mState.mCursorPosition.mLine - 1);
+			prevLine.insert(prevLine.end(), line.begin(), line.end());
+
+			ErrorMarkers etmp;
+			for (auto& i : mErrorMarkers)
+				etmp.insert(ErrorMarkers::value_type(i.first - 1 == mState.mCursorPosition.mLine ? i.first - 1 : i.first, i.second));
+			mErrorMarkers = std::move(etmp);
+
+			RemoveLine(mState.mCursorPosition.mLine);
+			--mState.mCursorPosition.mLine;
+			mState.mCursorPosition.mColumn = prevSize;
+		}
+		else
+		{
+			auto& line = mLines[mState.mCursorPosition.mLine];
+			auto cindex = GetCharacterIndex(pos) - 1;
+			auto cend = cindex + 1;
+			while (cindex > 0 && IsUTFSequence(line[cindex].mChar))
+				--cindex;
+
+			//if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1)
+			//	--cindex;
+
+			u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates();
+			--u.mRemovedStart.mColumn;
+			--mState.mCursorPosition.mColumn;
+
+			while (cindex < line.size() && cend-- > cindex)
+			{
+				u.mRemoved += line[cindex].mChar;
+				line.erase(line.begin() + cindex);
+			}
+		}
+
+		mTextChanged = true;
+
+		EnsureCursorVisible();
+		Colorize(mState.mCursorPosition.mLine, 1);
+	}
+
+	u.mAfter = mState;
+	AddUndo(u);
+}
+
+void TextEditor::SelectWordUnderCursor()
+{
+	auto c = GetCursorPosition();
+	SetSelection(FindWordStart(c), FindWordEnd(c));
+}
+
+void TextEditor::SelectAll()
+{
+	SetSelection(Coordinates(0, 0), Coordinates((int)mLines.size(), 0));
+}
+
+bool TextEditor::HasSelection() const
+{
+	return mState.mSelectionEnd > mState.mSelectionStart;
+}
+
+void TextEditor::Copy()
+{
+	if (HasSelection())
+	{
+		ImGui::SetClipboardText(GetSelectedText().c_str());
+	}
+	else
+	{
+		if (!mLines.empty())
+		{
+			std::string str;
+			auto& line = mLines[GetActualCursorCoordinates().mLine];
+			for (auto& g : line)
+				str.push_back(g.mChar);
+			ImGui::SetClipboardText(str.c_str());
+		}
+	}
+}
+
+void TextEditor::Cut()
+{
+	if (IsReadOnly())
+	{
+		Copy();
+	}
+	else
+	{
+		if (HasSelection())
+		{
+			UndoRecord u;
+			u.mBefore = mState;
+			u.mRemoved = GetSelectedText();
+			u.mRemovedStart = mState.mSelectionStart;
+			u.mRemovedEnd = mState.mSelectionEnd;
+
+			Copy();
+			DeleteSelection();
+
+			u.mAfter = mState;
+			AddUndo(u);
+		}
+	}
+}
+
+void TextEditor::Paste()
+{
+	if (IsReadOnly())
+		return;
+
+	auto clipText = ImGui::GetClipboardText();
+	if (clipText != nullptr && strlen(clipText) > 0)
+	{
+		UndoRecord u;
+		u.mBefore = mState;
+
+		if (HasSelection())
+		{
+			u.mRemoved = GetSelectedText();
+			u.mRemovedStart = mState.mSelectionStart;
+			u.mRemovedEnd = mState.mSelectionEnd;
+			DeleteSelection();
+		}
+
+		u.mAdded = clipText;
+		u.mAddedStart = GetActualCursorCoordinates();
+
+		InsertText(clipText);
+
+		u.mAddedEnd = GetActualCursorCoordinates();
+		u.mAfter = mState;
+		AddUndo(u);
+	}
+}
+
+bool TextEditor::CanUndo() const
+{
+	return !mReadOnly && mUndoIndex > 0;
+}
+
+bool TextEditor::CanRedo() const
+{
+	return !mReadOnly && mUndoIndex < (int)mUndoBuffer.size();
+}
+
+void TextEditor::Undo(int aSteps)
+{
+	while (CanUndo() && aSteps-- > 0)
+		mUndoBuffer[--mUndoIndex].Undo(this);
+}
+
+void TextEditor::Redo(int aSteps)
+{
+	while (CanRedo() && aSteps-- > 0)
+		mUndoBuffer[mUndoIndex++].Redo(this);
+}
+
+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
+			0xff206020, // Comment (single line)
+			0xff406020, // Comment (multi line)
+			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
+			0xff205020, // Comment (single line)
+			0xff405020, // Comment (multi line)
+			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
+			0xff808080, // Comment (single line)
+			0xff404040, // Comment (multi line)
+			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
+{
+	return GetText(Coordinates(), Coordinates((int)mLines.size(), 0));
+}
+
+std::vector<std::string> TextEditor::GetTextLines() const
+{
+	std::vector<std::string> result;
+
+	result.reserve(mLines.size());
+
+	for (auto & line : mLines)
+	{
+		std::string text;
+
+		text.resize(line.size());
+
+		for (size_t i = 0; i < line.size(); ++i)
+			text[i] = line[i].mChar;
+
+		result.emplace_back(std::move(text));
+	}
+
+	return result;
+}
+
+std::string TextEditor::GetSelectedText() const
+{
+	return GetText(mState.mSelectionStart, mState.mSelectionEnd);
+}
+
+std::string TextEditor::GetCurrentLineText()const
+{
+	auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine);
+	return GetText(
+		Coordinates(mState.mCursorPosition.mLine, 0),
+		Coordinates(mState.mCursorPosition.mLine, lineLength));
+}
+
+void TextEditor::ProcessInputs()
+{
+}
+
+void TextEditor::Colorize(int aFromLine, int aLines)
+{
+	int toLine = aLines == -1 ? (int)mLines.size() : std::min((int)mLines.size(), aFromLine + aLines);
+	mColorRangeMin = std::min(mColorRangeMin, aFromLine);
+	mColorRangeMax = std::max(mColorRangeMax, toLine);
+	mColorRangeMin = std::max(0, mColorRangeMin);
+	mColorRangeMax = std::max(mColorRangeMin, mColorRangeMax);
+	mCheckComments = true;
+}
+
+void TextEditor::ColorizeRange(int aFromLine, int aToLine)
+{
+	if (mLines.empty() || aFromLine >= aToLine)
+		return;
+
+	std::string buffer;
+	std::cmatch results;
+	std::string id;
+
+	int endLine = std::max(0, std::min((int)mLines.size(), aToLine));
+	for (int i = aFromLine; i < endLine; ++i)
+	{
+		auto& line = mLines[i];
+
+		if (line.empty())
+			continue;
+
+		buffer.resize(line.size());
+		for (size_t j = 0; j < line.size(); ++j)
+		{
+			auto& col = line[j];
+			buffer[j] = col.mChar;
+			col.mColorIndex = PaletteIndex::Default;
+		}
+
+		const char * bufferBegin = &buffer.front();
+		const char * bufferEnd = bufferBegin + buffer.size();
+
+		auto last = bufferEnd;
+
+		for (auto first = bufferBegin; first != last; )
+		{
+			const char * token_begin = nullptr;
+			const char * token_end = nullptr;
+			PaletteIndex token_color = PaletteIndex::Default;
+
+			bool hasTokenizeResult = false;
+
+			if (mLanguageDefinition.mTokenize != nullptr)
+			{
+				if (mLanguageDefinition.mTokenize(first, last, token_begin, token_end, token_color))
+					hasTokenizeResult = true;
+			}
+
+			if (hasTokenizeResult == false)
+			{
+				// todo : remove
+				//printf("using regex for %.*s\n", first + 10 < last ? 10 : int(last - first), first);
+
+				for (auto& p : mRegexList)
+				{
+					if (std::regex_search(first, last, results, p.first, std::regex_constants::match_continuous))
+					{
+						hasTokenizeResult = true;
+
+						auto& v = *results.begin();
+						token_begin = v.first;
+						token_end = v.second;
+						token_color = p.second;
+						break;
+					}
+				}
+			}
+
+			if (hasTokenizeResult == false)
+			{
+				first++;
+			}
+			else
+			{
+				const size_t token_length = token_end - token_begin;
+
+				if (token_color == PaletteIndex::Identifier)
+				{
+					id.assign(token_begin, token_end);
+
+					// todo : allmost all language definitions use lower case to specify keywords, so shouldn't this use ::tolower ?
+					if (!mLanguageDefinition.mCaseSensitive)
+						std::transform(id.begin(), id.end(), id.begin(), ::toupper);
+
+					if (!line[first - bufferBegin].mPreprocessor)
+					{
+						if (mLanguageDefinition.mKeywords.count(id) != 0)
+							token_color = PaletteIndex::Keyword;
+						else if (mLanguageDefinition.mIdentifiers.count(id) != 0)
+							token_color = PaletteIndex::KnownIdentifier;
+						else if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0)
+							token_color = PaletteIndex::PreprocIdentifier;
+					}
+					else
+					{
+						if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0)
+							token_color = PaletteIndex::PreprocIdentifier;
+					}
+				}
+
+				for (size_t j = 0; j < token_length; ++j)
+					line[(token_begin - bufferBegin) + j].mColorIndex = token_color;
+
+				first = token_end;
+			}
+		}
+	}
+}
+
+void TextEditor::ColorizeInternal()
+{
+	if (mLines.empty() || !mColorizerEnabled)
+		return;
+
+	if (mCheckComments)
+	{
+		auto endLine = mLines.size();
+		auto endIndex = 0;
+		auto commentStartLine = endLine;
+		auto commentStartIndex = endIndex;
+		auto withinString = false;
+		auto withinSingleLineComment = false;
+		auto withinPreproc = false;
+		auto firstChar = true;			// there is no other non-whitespace characters in the line before
+		auto concatenate = false;		// '\' on the very end of the line
+		auto currentLine = 0;
+		auto currentIndex = 0;
+		while (currentLine < endLine || currentIndex < endIndex)
+		{
+			auto& line = mLines[currentLine];
+
+			if (currentIndex == 0 && !concatenate)
+			{
+				withinSingleLineComment = false;
+				withinPreproc = false;
+				firstChar = true;
+			}
+
+			concatenate = false;
+
+			if (!line.empty())
+			{
+				auto& g = line[currentIndex];
+				auto c = g.mChar;
+
+				if (c != mLanguageDefinition.mPreprocChar && !isspace(c))
+					firstChar = false;
+
+				if (currentIndex == (int)line.size() - 1 && line[line.size() - 1].mChar == '\\')
+					concatenate = true;
+
+				bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
+
+				if (withinString)
+				{
+					line[currentIndex].mMultiLineComment = inComment;
+
+					if (c == '\"')
+					{
+						if (currentIndex + 1 < (int)line.size() && line[currentIndex + 1].mChar == '\"')
+						{
+							currentIndex += 1;
+							if (currentIndex < (int)line.size())
+								line[currentIndex].mMultiLineComment = inComment;
+						}
+						else
+							withinString = false;
+					}
+					else if (c == '\\')
+					{
+						currentIndex += 1;
+						if (currentIndex < (int)line.size())
+							line[currentIndex].mMultiLineComment = inComment;
+					}
+				}
+				else
+				{
+					if (firstChar && c == mLanguageDefinition.mPreprocChar)
+						withinPreproc = true;
+
+					if (c == '\"')
+					{
+						withinString = true;
+						line[currentIndex].mMultiLineComment = inComment;
+					}
+					else
+					{
+						auto pred = [](const char& a, const Glyph& b) { return a == b.mChar; };
+						auto from = line.begin() + currentIndex;
+						auto& startStr = mLanguageDefinition.mCommentStart;
+						auto& singleStartStr = mLanguageDefinition.mSingleLineComment;
+
+						if (singleStartStr.size() > 0 &&
+							currentIndex + singleStartStr.size() <= line.size() &&
+							equals(singleStartStr.begin(), singleStartStr.end(), from, from + singleStartStr.size(), pred))
+						{
+							withinSingleLineComment = true;
+						}
+						else if (!withinSingleLineComment && currentIndex + startStr.size() <= line.size() &&
+							equals(startStr.begin(), startStr.end(), from, from + startStr.size(), pred))
+						{
+							commentStartLine = currentLine;
+							commentStartIndex = currentIndex;
+						}
+
+						inComment = inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
+
+						line[currentIndex].mMultiLineComment = inComment;
+						line[currentIndex].mComment = withinSingleLineComment;
+
+						auto& endStr = mLanguageDefinition.mCommentEnd;
+						if (currentIndex + 1 >= (int)endStr.size() &&
+							equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(), from + 1, pred))
+						{
+							commentStartIndex = endIndex;
+							commentStartLine = endLine;
+						}
+					}
+				}
+				line[currentIndex].mPreprocessor = withinPreproc;
+				currentIndex += UTF8CharLength(c);
+				if (currentIndex >= (int)line.size())
+				{
+					currentIndex = 0;
+					++currentLine;
+				}
+			}
+			else
+			{
+				currentIndex = 0;
+				++currentLine;
+			}
+		}
+		mCheckComments = false;
+	}
+
+	if (mColorRangeMin < mColorRangeMax)
+	{
+		const int increment = (mLanguageDefinition.mTokenize == nullptr) ? 10 : 10000;
+		const int to = std::min(mColorRangeMin + increment, mColorRangeMax);
+		ColorizeRange(mColorRangeMin, to);
+		mColorRangeMin = to;
+
+		if (mColorRangeMax == mColorRangeMin)
+		{
+			mColorRangeMin = std::numeric_limits<int>::max();
+			mColorRangeMax = 0;
+		}
+		return;
+	}
+}
+
+float TextEditor::TextDistanceToLineStart(const Coordinates& aFrom) const
+{
+	auto& line = mLines[aFrom.mLine];
+	float distance = 0.0f;
+	float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x;
+	int colIndex = GetCharacterIndex(aFrom);
+	for (size_t it = 0u; it < line.size() && it < colIndex; )
+	{
+		if (line[it].mChar == '\t')
+		{
+			distance = (1.0f + std::floor((1.0f + distance) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);
+			++it;
+		}
+		else
+		{
+			auto d = UTF8CharLength(line[it].mChar);
+			char tempCString[7];
+			int i = 0;
+			for (; i < 6 && d-- > 0 && it < (int)line.size(); i++, it++)
+				tempCString[i] = line[it].mChar;
+
+			tempCString[i] = '\0';
+			distance += ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, tempCString, nullptr, nullptr).x;
+		}
+	}
+
+	return distance;
+}
+
+void TextEditor::EnsureCursorVisible()
+{
+	if (!mWithinRender)
+	{
+		mScrollToCursor = true;
+		return;
+	}
+
+	float scrollX = ImGui::GetScrollX();
+	float scrollY = ImGui::GetScrollY();
+
+	auto height = ImGui::GetWindowHeight();
+	auto width = ImGui::GetWindowWidth();
+
+	auto top = 1 + (int)ceil(scrollY / mCharAdvance.y);
+	auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y);
+
+	auto left = (int)ceil(scrollX / mCharAdvance.x);
+	auto right = (int)ceil((scrollX + width) / mCharAdvance.x);
+
+	auto pos = GetActualCursorCoordinates();
+	auto len = TextDistanceToLineStart(pos);
+
+	if (pos.mLine < top)
+		ImGui::SetScrollY(std::max(0.0f, (pos.mLine - 1) * mCharAdvance.y));
+	if (pos.mLine > bottom - 4)
+		ImGui::SetScrollY(std::max(0.0f, (pos.mLine + 4) * mCharAdvance.y - height));
+	if (len + mTextStart < left + 4)
+		ImGui::SetScrollX(std::max(0.0f, len + mTextStart - 4));
+	if (len + mTextStart > right - 4)
+		ImGui::SetScrollX(std::max(0.0f, len + mTextStart + 4 - width));
+}
+
+int TextEditor::GetPageSize() const
+{
+	auto height = ImGui::GetWindowHeight() - 20.0f;
+	return (int)floor(height / mCharAdvance.y);
+}
+
+TextEditor::UndoRecord::UndoRecord(
+	const std::string& aAdded,
+	const TextEditor::Coordinates aAddedStart,
+	const TextEditor::Coordinates aAddedEnd,
+	const std::string& aRemoved,
+	const TextEditor::Coordinates aRemovedStart,
+	const TextEditor::Coordinates aRemovedEnd,
+	TextEditor::EditorState& aBefore,
+	TextEditor::EditorState& aAfter)
+	: mAdded(aAdded)
+	, mAddedStart(aAddedStart)
+	, mAddedEnd(aAddedEnd)
+	, mRemoved(aRemoved)
+	, mRemovedStart(aRemovedStart)
+	, mRemovedEnd(aRemovedEnd)
+	, mBefore(aBefore)
+	, mAfter(aAfter)
+{
+	assert(mAddedStart <= mAddedEnd);
+	assert(mRemovedStart <= mRemovedEnd);
+}
+
+void TextEditor::UndoRecord::Undo(TextEditor * aEditor)
+{
+	if (!mAdded.empty())
+	{
+		aEditor->DeleteRange(mAddedStart, mAddedEnd);
+		aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 2);
+	}
+
+	if (!mRemoved.empty())
+	{
+		auto start = mRemovedStart;
+		aEditor->InsertTextAt(start, mRemoved.c_str());
+		aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 2);
+	}
+
+	aEditor->mState = mBefore;
+	aEditor->EnsureCursorVisible();
+
+}
+
+void TextEditor::UndoRecord::Redo(TextEditor * aEditor)
+{
+	if (!mRemoved.empty())
+	{
+		aEditor->DeleteRange(mRemovedStart, mRemovedEnd);
+		aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 1);
+	}
+
+	if (!mAdded.empty())
+	{
+		auto start = mAddedStart;
+		aEditor->InsertTextAt(start, mAdded.c_str());
+		aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 1);
+	}
+
+	aEditor->mState = mAfter;
+	aEditor->EnsureCursorVisible();
+}
+
+static bool TokenizeCStyleString(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
+{
+	const char * 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++;
+
+			p++;
+		}
+	}
+
+	return false;
+}
+
+static bool TokenizeCStyleCharacterLiteral(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
+{
+	const char * 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;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static bool TokenizeCStyleIdentifier(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
+{
+	const char * p = in_begin;
+
+	if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_')
+	{
+		p++;
+
+		while ((p < in_end) && ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_'))
+			p++;
+
+		out_begin = in_begin;
+		out_end = p;
+		return true;
+	}
+
+	return false;
+}
+
+static bool TokenizeCStyleNumber(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
+{
+	const char * p = in_begin;
+
+	const bool startsWithNumber = *p >= '0' && *p <= '9';
+
+	if (*p != '+' && *p != '-' && !startsWithNumber)
+		return false;
+
+	p++;
+
+	bool hasNumber = startsWithNumber;
+
+	while (p < in_end && (*p >= '0' && *p <= '9'))
+	{
+		hasNumber = true;
+
+		p++;
+	}
+
+	if (hasNumber == false)
+		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++;
+		}
+		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 == false && isBinary == false)
+	{
+		// 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 == false)
+				return false;
+		}
+
+		// single precision floating point type
+		if (p < in_end && *p == 'f')
+			p++;
+	}
+
+	if (isFloat == false)
+	{
+		// integer size type
+		while (p < in_end && (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L'))
+			p++;
+	}
+
+	out_begin = in_begin;
+	out_end = p;
+	return true;
+}
+
+static bool TokenizeCStylePunctuation(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
+{
+	(void)in_end;
+
+	switch (*in_begin)
+	{
+	case '[':
+	case ']':
+	case '{':
+	case '}':
+	case '!':
+	case '%':
+	case '^':
+	case '&':
+	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;
+}
+
+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.mKeywords.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.mDeclaration = "Built-in function";
+			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
+		}
+
+		langDef.mTokenize = [](const char * in_begin, const char * in_end, const char *& out_begin, const char *& 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::String;
+			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::Number;
+			else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end))
+				paletteIndex = PaletteIndex::Punctuation;
+
+			return paletteIndex != PaletteIndex::Max;
+		};
+
+		langDef.mCommentStart = "/*";
+		langDef.mCommentEnd = "*/";
+		langDef.mSingleLineComment = "//";
+
+		langDef.mCaseSensitive = true;
+		langDef.mAutoIndentation = true;
+
+		langDef.mName = "C++";
+
+		inited = true;
+	}
+	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.mKeywords.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.mDeclaration = "Built-in function";
+			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
+		}
+
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
+
+		langDef.mCommentStart = "/*";
+		langDef.mCommentEnd = "*/";
+		langDef.mSingleLineComment = "//";
+
+		langDef.mCaseSensitive = true;
+		langDef.mAutoIndentation = true;
+
+		langDef.mName = "HLSL";
+
+		inited = true;
+	}
+	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.mKeywords.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.mDeclaration = "Built-in function";
+			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
+		}
+
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
+
+		langDef.mCommentStart = "/*";
+		langDef.mCommentEnd = "*/";
+		langDef.mSingleLineComment = "//";
+
+		langDef.mCaseSensitive = true;
+		langDef.mAutoIndentation = true;
+
+		langDef.mName = "GLSL";
+
+		inited = true;
+	}
+	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.mKeywords.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.mDeclaration = "Built-in function";
+			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
+		}
+
+		langDef.mTokenize = [](const char * in_begin, const char * in_end, const char *& out_begin, const char *& 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::String;
+			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::Number;
+			else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end))
+				paletteIndex = PaletteIndex::Punctuation;
+
+			return paletteIndex != PaletteIndex::Max;
+		};
+
+		langDef.mCommentStart = "/*";
+		langDef.mCommentEnd = "*/";
+		langDef.mSingleLineComment = "//";
+
+		langDef.mCaseSensitive = true;
+		langDef.mAutoIndentation = true;
+
+		langDef.mName = "C";
+
+		inited = true;
+	}
+	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"
+		};
+
+		for (auto& k : keywords)
+			langDef.mKeywords.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.mDeclaration = "Built-in function";
+			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
+		}
+
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'", PaletteIndex::String));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
+
+		langDef.mCommentStart = "/*";
+		langDef.mCommentEnd = "*/";
+		langDef.mSingleLineComment = "//";
+
+		langDef.mCaseSensitive = false;
+		langDef.mAutoIndentation = false;
+
+		langDef.mName = "SQL";
+
+		inited = true;
+	}
+	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"
+		};
+
+		for (auto& k : keywords)
+			langDef.mKeywords.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.mDeclaration = "Built-in function";
+			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
+		}
+
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::String));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
+
+		langDef.mCommentStart = "/*";
+		langDef.mCommentEnd = "*/";
+		langDef.mSingleLineComment = "//";
+
+		langDef.mCaseSensitive = true;
+		langDef.mAutoIndentation = true;
+
+		langDef.mName = "AngelScript";
+
+		inited = true;
+	}
+	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.mKeywords.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.mDeclaration = "Built-in function";
+			langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
+		}
+
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'", PaletteIndex::String));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
+		langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
+
+		langDef.mCommentStart = "--[[";
+		langDef.mCommentEnd = "]]";
+		langDef.mSingleLineComment = "--";
+
+		langDef.mCaseSensitive = true;
+		langDef.mAutoIndentation = false;
+
+		langDef.mName = "Lua";
+
+		inited = true;
+	}
+	return langDef;
+}