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