From 00618603414a7618a666d5518baf0e362189a6d4 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Thu, 14 Jun 2018 15:30:02 +0200 Subject: [PATCH 01/26] FIX: Undo caused crash when inserting newline at the end of text --- TextEditor.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 14d3cff7..c2afb864 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -192,16 +192,23 @@ int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValu } else if (chr == '\n') { - if (aWhere.mColumn < (int)mLines[aWhere.mLine].size()) + if (aWhere.mLine < mLines.size()) { - auto& newLine = InsertLine(aWhere.mLine + 1); - auto& line = mLines[aWhere.mLine]; - newLine.insert(newLine.begin(), line.begin() + aWhere.mColumn, line.end()); - line.erase(line.begin() + aWhere.mColumn, line.end()); + if (aWhere.mColumn < (int)mLines[aWhere.mLine].size()) + { + auto& newLine = InsertLine(aWhere.mLine + 1); + auto& line = mLines[aWhere.mLine]; + newLine.insert(newLine.begin(), line.begin() + aWhere.mColumn, line.end()); + line.erase(line.begin() + aWhere.mColumn, line.end()); + } + else + { + InsertLine(aWhere.mLine + 1); + } } else { - InsertLine(aWhere.mLine + 1); + InsertLine(aWhere.mLine); } ++aWhere.mLine; aWhere.mColumn = 0; From 5f2bf490770817e3181b49aa1174c112dfd8b589 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Thu, 14 Jun 2018 20:54:55 +0200 Subject: [PATCH 02/26] Text input cursor only visible when hovering over the text. Text only reacting to clicks when pointing at the text. Now auto scrolling up and down when dragging cursor outside the text area. --- TextEditor.cpp | 188 ++++++++++++++++++++++++++++++++++++++----------- TextEditor.h | 3 + 2 files changed, 151 insertions(+), 40 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index c2afb864..6cbc9d5c 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -5,6 +5,7 @@ #include #include "TextEditor.h" +#include "imgui_internal.h" static const int cTextStart = 7; @@ -410,6 +411,62 @@ std::string TextEditor::GetWordAt(const Coordinates & aCoords) const return r; } +bool TextEditor::MouseOverText() const +{ + const auto mousePos = ImGui::GetMousePos(); + const auto cursorScreenPos = ImGui::GetCursorScreenPos(); + const auto contentSize = ImGui::GetContentRegionMax(); + const auto scrollX = ImGui::GetScrollX(); + const auto scrollY = ImGui::GetScrollY(); + + const auto breakAndLineNumberWidth = mCharAdvance.x * cTextStart; + const auto areaA = ImVec2(cursorScreenPos.x + breakAndLineNumberWidth, cursorScreenPos.y + scrollY); + const auto areaB = ImVec2(cursorScreenPos.x + contentSize.x + scrollX * 2, areaA.y + contentSize.y + scrollY); + return ImRect(areaA, areaB).Contains(mousePos); +} + +ImVec2 TextEditor::MouseDistanceOutsideTextArea() const +{ + const auto mousePos = ImGui::GetMousePos(); + const auto cursorScreenPos = ImGui::GetCursorScreenPos(); + const auto contentSize = ImGui::GetContentRegionMax(); + const auto scrollX = ImGui::GetScrollX(); + const auto scrollY = ImGui::GetScrollY(); + + const auto breakAndLineNumberWidth = mCharAdvance.x * cTextStart; + const auto areaA = ImVec2(cursorScreenPos.x + breakAndLineNumberWidth, cursorScreenPos.y + scrollY); + const auto areaB = ImVec2(cursorScreenPos.x + contentSize.x + scrollX * 2, areaA.y + contentSize.y + scrollY); + + const auto textArea = ImRect(areaA, areaB); + if(textArea.Contains(mousePos)) + { + return {0,0}; + } + + float x = 0; + float y = 0; + + if(mousePos.x < textArea.Min.x) + { + x = mousePos.x - textArea.Min.x; + } + else if(mousePos.x > textArea.Max.x) + { + x = mousePos.x - textArea.Max.x; + } + + if (mousePos.y < textArea.Min.y) + { + y = mousePos.y - textArea.Min.y; + } + else if (mousePos.y > textArea.Max.y) + { + y = mousePos.y - textArea.Max.y; + } + + return { x,y }; +} + void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) { mWithinRender = true; @@ -429,10 +486,16 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) auto ctrl = io.KeyCtrl; auto alt = io.KeyAlt; - if (ImGui::IsWindowFocused()) + if (ImGui::IsWindowHovered()) { - if (ImGui::IsWindowHovered()) + if (MouseOverText()) + { ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); + } + } + + if (ImGui::IsWindowFocused()) + { //ImGui::CaptureKeyboardFromApp(true); io.WantCaptureKeyboard = true; @@ -506,67 +569,112 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) } } + static bool textAreaHeld = false; if (ImGui::IsWindowHovered()) { static float lastClick = -1.0f; + if (!shift && !alt) { - auto click = ImGui::IsMouseClicked(0); - auto doubleClick = ImGui::IsMouseDoubleClicked(0); - auto t = ImGui::GetTime(); - auto tripleClick = click && !doubleClick && t - lastClick < io.MouseDoubleClickTime; - if (tripleClick) + if (MouseOverText()) { - printf("triple\n"); - if (!ctrl) + auto click = ImGui::IsMouseClicked(0); + auto doubleClick = ImGui::IsMouseDoubleClicked(0); + auto t = ImGui::GetTime(); + auto tripleClick = click && !doubleClick && t - lastClick < io.MouseDoubleClickTime; + if (tripleClick) { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); - mSelectionMode = SelectionMode::Line; - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + printf("triple\n"); + if (!ctrl) + { + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); + mSelectionMode = SelectionMode::Line; + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + } + + lastClick = -1.0f; } + else if (doubleClick) + { + printf("double\n"); + 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); + } - lastClick = -1.0f; - } - else if (doubleClick) - { - printf("double\n"); - if (!ctrl) + lastClick = ImGui::GetTime(); + } + else if (click) { + printf("single\n"); mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); - if (mSelectionMode == SelectionMode::Line) - mSelectionMode = SelectionMode::Normal; - else + if (ctrl) mSelectionMode = SelectionMode::Word; + else + mSelectionMode = SelectionMode::Normal; SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + + lastClick = ImGui::GetTime(); } - lastClick = ImGui::GetTime(); + textAreaHeld = click || ImGui::IsMouseDown(0); } - else if (click) - { - printf("single\n"); - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); - if (ctrl) - mSelectionMode = SelectionMode::Word; - else - mSelectionMode = SelectionMode::Normal; - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + } - lastClick = ImGui::GetTime(); - } - else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) + //if (!ImGui::IsMouseDown(0)) + // mWordSelectionMode = false; + } + + // Continue to select text while mouse is held + if (textAreaHeld) + { + const float scrollCounterMax = 4.0f; + static float scrollVerticalCounter = scrollCounterMax; + + // Not holding the button down anymore? + if(!ImGui::IsMouseDown(0)) + { + textAreaHeld = false; + //Reset counter + scrollVerticalCounter = scrollCounterMax; + } + else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) + { + if(!MouseOverText()) { - io.WantCaptureMouse = true; - mState.mCursorPosition = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + // TODO Perform horizontal scoll + + auto dist = MouseDistanceOutsideTextArea(); + + if(dist.y) + { + scrollVerticalCounter -= ImGui::GetIO().DeltaTime * abs(dist.y); + + if(scrollVerticalCounter < 0.0f) + { + EnsureCursorVisible(); + scrollVerticalCounter = scrollCounterMax; + } + } + else + { + scrollVerticalCounter = scrollCounterMax; + } } else { + scrollVerticalCounter = scrollCounterMax; } - } - //if (!ImGui::IsMouseDown(0)) - // mWordSelectionMode = false; + io.WantCaptureMouse = true; + mState.mCursorPosition = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + } } ColorizeInternal(); diff --git a/TextEditor.h b/TextEditor.h index 9613b513..3daa1f48 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -295,6 +295,9 @@ class TextEditor std::string GetWordUnderCursor() const; std::string GetWordAt(const Coordinates& aCoords) const; + bool MouseOverText() const; + ImVec2 MouseDistanceOutsideTextArea() const; + float mLineSpacing; Lines mLines; EditorState mState; From bec5effd6af75de94471cf0d99d6a876daac606b Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Thu, 14 Jun 2018 22:16:21 +0200 Subject: [PATCH 03/26] Added a save index, used in combination with the undo buffer, to mark the text as dirty. Useful for being able to save to file. --- TextEditor.cpp | 19 +++++++++++++++++++ TextEditor.h | 5 ++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 6cbc9d5c..f288fd12 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -30,6 +30,7 @@ bool equals(InputIt1 first1, InputIt1 last1, TextEditor::TextEditor() : mLineSpacing(0.0f) , mUndoIndex(0) + , mUndoSaveIndex(0) , mTabSize(4) , mOverwrite(false) , mReadOnly(false) @@ -233,6 +234,12 @@ void TextEditor::AddUndo(UndoRecord& aValue) { assert(!mReadOnly); + // Is save index lost? + if(mUndoSaveIndex > mUndoIndex) + { + mUndoSaveIndex = -1; + } + mUndoBuffer.resize(mUndoIndex + 1); mUndoBuffer.back() = aValue; ++mUndoIndex; @@ -874,6 +881,8 @@ void TextEditor::SetText(const std::string & aText) mTextChanged = true; } + mUndoIndex = 0; + mUndoSaveIndex = 0; mUndoBuffer.clear(); Colorize(); @@ -1483,6 +1492,16 @@ void TextEditor::Redo(int aSteps) mUndoBuffer[mUndoIndex++].Redo(this); } +void TextEditor::MarkSaved() +{ + mUndoSaveIndex = mUndoIndex; +} + +bool TextEditor::IsDirty() const +{ + return mUndoSaveIndex != mUndoIndex; +} + const TextEditor::Palette & TextEditor::GetDarkPalette() { static Palette p = { { diff --git a/TextEditor.h b/TextEditor.h index 3daa1f48..0da6bb5e 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -217,6 +217,8 @@ class TextEditor bool CanRedo() const; void Undo(int aSteps = 1); void Redo(int aSteps = 1); + void MarkSaved(); + bool IsDirty() const; static const Palette& GetDarkPalette(); static const Palette& GetLightPalette(); @@ -303,7 +305,8 @@ class TextEditor EditorState mState; UndoBuffer mUndoBuffer; int mUndoIndex; - + int mUndoSaveIndex; + int mTabSize; bool mOverwrite; bool mReadOnly; From 337ea5d831e5124fed6737629bbfe34f704a9fa8 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Fri, 15 Jun 2018 17:19:56 +0200 Subject: [PATCH 04/26] Added a breakpoint bar, with actual breakpoints, instead of just line highlight Added the abillity to toggle breakpoints. Added a breakpoints changed callback. --- TextEditor.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++++-- TextEditor.h | 8 ++++++- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index f288fd12..f04eaada 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -41,6 +41,8 @@ TextEditor::TextEditor() , mColorRangeMax(0) , mSelectionMode(SelectionMode::Normal) , mCheckMultilineComments(true) + , mBreakpointsModified(false) + , mBreakpointsModifiedCallback(nullptr) { SetPalette(GetDarkPalette()); SetLanguageDefinition(LanguageDefinition::HLSL()); @@ -270,6 +272,13 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPositi return Coordinates(lineNo, column); } +unsigned int TextEditor::ScreenPosToLineNumber(const ImVec2& aPosition) const +{ + const ImVec2 origin = ImGui::GetCursorScreenPos(); + const unsigned int lineNo = std::max(0, static_cast(floor((aPosition.y - origin.y) / mCharAdvance.y))); + return lineNo + 1; +} + TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates & aFrom) const { Coordinates at = aFrom; @@ -432,6 +441,20 @@ bool TextEditor::MouseOverText() const return ImRect(areaA, areaB).Contains(mousePos); } +bool TextEditor::MouseOverBreakpoints() const +{ + const auto mousePos = ImGui::GetMousePos(); + const auto cursorScreenPos = ImGui::GetCursorScreenPos(); + const auto contentSize = ImGui::GetContentRegionMax(); + const auto scrollY = ImGui::GetScrollY(); + + const float breakpoinBarWidth = mCharAdvance.y; + auto breakBarA = ImVec2(cursorScreenPos.x, cursorScreenPos.y + scrollY); + auto breakBarB = ImVec2(breakBarA.x + breakpoinBarWidth, breakBarA.y + contentSize.y + scrollY); + + return ImRect(breakBarA, breakBarB).Contains(mousePos); +} + ImVec2 TextEditor::MouseDistanceOutsideTextArea() const { const auto mousePos = ImGui::GetMousePos(); @@ -631,6 +654,27 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) textAreaHeld = click || ImGui::IsMouseDown(0); } + else if(MouseOverBreakpoints()) // Check for breakpoint changes + { + if(ImGui::IsMouseClicked(0)) + { + auto line = ScreenPosToLineNumber(ImGui::GetMousePos()); + if(line < mLines.size() + 1) + { + const auto found = mBreakpoints.find(line); + if(found == mBreakpoints.end()) + { + mBreakpoints.insert(line); + } + else + { + mBreakpoints.erase(found); + } + + mBreakpointsModified = true; + } + } + } } //if (!ImGui::IsMouseDown(0)) @@ -735,8 +779,9 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) if (mBreakpoints.find(lineNo + 1) != mBreakpoints.end()) { - auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]); + const float breakpointRadius = mCharAdvance.y / 2 - 2; + ImVec2 circlePos = { lineStartScreenPos.x + breakpointRadius + 2, lineStartScreenPos.y + breakpointRadius + 2 }; + drawList->AddCircleFilled(circlePos, (mCharAdvance.y / 2) - 2, 0xff0000ff); } auto errorIt = mErrorMarkers.find(lineNo + 1); @@ -856,6 +901,15 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) mScrollToCursor = false; } + if(mBreakpointsModified) + { + if(mBreakpointsModifiedCallback) + { + mBreakpointsModifiedCallback(this); + } + mBreakpointsModified = false; + } + ImGui::PopAllowKeyboardFocus(); ImGui::EndChild(); ImGui::PopStyleVar(); diff --git a/TextEditor.h b/TextEditor.h index 0da6bb5e..e25599cb 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -125,6 +125,7 @@ class TextEditor typedef std::unordered_set Breakpoints; typedef std::array Palette; typedef char Char; + using BreakpointsModifiedCallback = void(*)(TextEditor*); struct Glyph { @@ -172,7 +173,8 @@ class TextEditor void SetPalette(const Palette& aValue); void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; } - void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers; } + void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers ; mBreakpointsModified = true; } + void SetBreakPointsChangedCallback(BreakpointsModifiedCallback callback) { mBreakpointsModifiedCallback = callback; } void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); void SetText(const std::string& aText); @@ -285,6 +287,7 @@ class TextEditor int InsertTextAt(Coordinates& aWhere, const char* aValue); void AddUndo(UndoRecord& aValue); Coordinates ScreenPosToCoordinates(const ImVec2& aPosition) const; + unsigned int ScreenPosToLineNumber(const ImVec2& aPosition) const; Coordinates FindWordStart(const Coordinates& aFrom) const; Coordinates FindWordEnd(const Coordinates& aFrom) const; bool IsOnWordBoundary(const Coordinates& aAt) const; @@ -298,6 +301,7 @@ class TextEditor std::string GetWordAt(const Coordinates& aCoords) const; bool MouseOverText() const; + bool MouseOverBreakpoints() const; ImVec2 MouseDistanceOutsideTextArea() const; float mLineSpacing; @@ -322,6 +326,8 @@ class TextEditor bool mCheckMultilineComments; Breakpoints mBreakpoints; + bool mBreakpointsModified; + BreakpointsModifiedCallback mBreakpointsModifiedCallback; ErrorMarkers mErrorMarkers; ImVec2 mCharAdvance; Coordinates mInteractiveStart, mInteractiveEnd; From e4a6449285d551e6d3687ce56352a43f5931800e Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Sat, 16 Jun 2018 17:33:36 +0200 Subject: [PATCH 05/26] FIX: SetText not inserting a line when passed empty string --- TextEditor.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 14d3cff7..d6a51238 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -745,18 +745,25 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) void TextEditor::SetText(const std::string & aText) { mLines.clear(); - for (auto chr : aText) + if (aText.empty()) { - if (mLines.empty()) - mLines.push_back(Line()); - if (chr == '\n') - mLines.push_back(Line()); - else + mLines.push_back(Line()); + } + else + { + for (auto chr : aText) { - mLines.back().push_back(Glyph(chr, PaletteIndex::Default)); - } + if (mLines.empty()) + mLines.push_back(Line()); + if (chr == '\n') + mLines.push_back(Line()); + else + { + mLines.back().push_back(Glyph(chr, PaletteIndex::Default)); + } - mTextChanged = true; + mTextChanged = true; + } } mUndoBuffer.clear(); From 42d6cda5b5d18ae40671be8c3c71b4f965d5c8da Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Sun, 17 Jun 2018 16:23:41 +0200 Subject: [PATCH 06/26] Added GetBreakpoints method --- TextEditor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/TextEditor.h b/TextEditor.h index e25599cb..261a35b4 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -174,6 +174,7 @@ class TextEditor void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; } void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers ; mBreakpointsModified = true; } + const Breakpoints& GetBreakpoints() const { return mBreakpoints; } void SetBreakPointsChangedCallback(BreakpointsModifiedCallback callback) { mBreakpointsModifiedCallback = callback; } void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); From eca6275e554651acc2e66922a3c0335125958dd0 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Wed, 20 Jun 2018 19:50:34 +0200 Subject: [PATCH 07/26] Added a statement marker --- TextEditor.cpp | 25 +++++++++++++++++++++++++ TextEditor.h | 4 +++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 7ec9952a..afa6415a 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -41,6 +41,7 @@ TextEditor::TextEditor() , mColorRangeMax(0) , mSelectionMode(SelectionMode::Normal) , mCheckMultilineComments(true) + , mCurrentStatement(-1) , mBreakpointsModified(false) , mBreakpointsModifiedCallback(nullptr) { @@ -68,6 +69,11 @@ void TextEditor::SetPalette(const Palette & aValue) mPalette = aValue; } +void TextEditor::SetStatementMarker(int line) +{ + mCurrentStatement = line; +} + int TextEditor::AppendBuffer(std::string& aBuffer, char chr, int aIndex) { if (chr != '\t') @@ -784,6 +790,25 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) drawList->AddCircleFilled(circlePos, (mCharAdvance.y / 2) - 2, 0xff0000ff); } + // Draw statement marker + if(mCurrentStatement - 1 == lineNo) + { + const float breakpointRadius = mCharAdvance.y / 2 - 2; + const float triangleRadius = breakpointRadius - 1; + ImVec2 circlePos = { lineStartScreenPos.x + breakpointRadius + 2, lineStartScreenPos.y + breakpointRadius + 2 }; + + float rad = IM_PI * 2.0f / 3.0f; + float x = cos(rad) * triangleRadius; + float y = sin(rad) * triangleRadius; + + ImVec2 a = circlePos; + a.x += triangleRadius; + ImVec2 b = { circlePos.x + x, circlePos.y - y }; + ImVec2 c = { circlePos.x + x, circlePos.y + y }; + + drawList->AddTriangleFilled(a,b,c, 0xff00ddff); + } + auto errorIt = mErrorMarkers.find(lineNo + 1); if (errorIt != mErrorMarkers.end()) { diff --git a/TextEditor.h b/TextEditor.h index 261a35b4..a5c2507a 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -171,7 +171,8 @@ class TextEditor const Palette& GetPalette() const { return mPalette; } void SetPalette(const Palette& aValue); - + + void SetStatementMarker(int line); void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; } void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers ; mBreakpointsModified = true; } const Breakpoints& GetBreakpoints() const { return mBreakpoints; } @@ -326,6 +327,7 @@ class TextEditor RegexList mRegexList; bool mCheckMultilineComments; + int mCurrentStatement; Breakpoints mBreakpoints; bool mBreakpointsModified; BreakpointsModifiedCallback mBreakpointsModifiedCallback; From 1185d1125bcd97cfab76f5d9c3276937831bc5c0 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Mon, 25 Jun 2018 21:39:33 +0200 Subject: [PATCH 08/26] Setting statement marker now enures that the line is scrolled to if not on screen --- TextEditor.cpp | 37 +++++++++++++++++++++++++++++++++++++ TextEditor.h | 2 ++ 2 files changed, 39 insertions(+) diff --git a/TextEditor.cpp b/TextEditor.cpp index afa6415a..030eef93 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -36,6 +36,7 @@ TextEditor::TextEditor() , mReadOnly(false) , mWithinRender(false) , mScrollToCursor(false) + , mScrollToStatementMarker(false) , mTextChanged(false) , mColorRangeMin(0) , mColorRangeMax(0) @@ -72,6 +73,11 @@ void TextEditor::SetPalette(const Palette & aValue) void TextEditor::SetStatementMarker(int line) { mCurrentStatement = line; + + if(mCurrentStatement != -1) + { + mScrollToStatementMarker = true; + } } int TextEditor::AppendBuffer(std::string& aBuffer, char chr, int aIndex) @@ -926,6 +932,13 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) mScrollToCursor = false; } + if(mScrollToStatementMarker) + { + EnsureLineVisible(mCurrentStatement); + ImGui::SetWindowFocus(); + mScrollToStatementMarker = false; + } + if(mBreakpointsModified) { if(mBreakpointsModifiedCallback) @@ -1895,6 +1908,30 @@ void TextEditor::EnsureCursorVisible() ImGui::SetScrollX(std::max(0.0f, (len + cTextStart + 4) * mCharAdvance.x - width)); } +void TextEditor::EnsureLineVisible(int line) +{ + if (!mWithinRender) + { + mScrollToStatementMarker = true; + return; + } + + float scrollY = ImGui::GetScrollY(); + + auto height = ImGui::GetWindowHeight(); + + auto top = 1 + (int)ceil(scrollY / mCharAdvance.y); + auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y); + + auto pos = SanitizeCoordinates({line, 0}); + + if (line < top) + ImGui::SetScrollY(std::max(0.0f, (pos.mLine + (height / mCharAdvance.y / 2.0f)) * mCharAdvance.y - height)); + if (line > bottom) + ImGui::SetScrollY(std::max(0.0f, (pos.mLine + (height / mCharAdvance.y / 2.0f)) * mCharAdvance.y - height)); + ImGui::SetScrollX(0); +} + int TextEditor::GetPageSize() const { auto height = ImGui::GetWindowHeight() - 20.0f; diff --git a/TextEditor.h b/TextEditor.h index a5c2507a..62db7873 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -279,6 +279,7 @@ class TextEditor void ColorizeInternal(); int TextDistanceToLineStart(const Coordinates& aFrom) const; void EnsureCursorVisible(); + void EnsureLineVisible(int line); int GetPageSize() const; int AppendBuffer(std::string& aBuffer, char chr, int aIndex); std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const; @@ -318,6 +319,7 @@ class TextEditor bool mReadOnly; bool mWithinRender; bool mScrollToCursor; + bool mScrollToStatementMarker; bool mTextChanged; int mColorRangeMin, mColorRangeMax; SelectionMode mSelectionMode; From 53186371e75b44438b4baab88299b6e502ed4c08 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Thu, 28 Jun 2018 19:10:30 +0200 Subject: [PATCH 09/26] Made scroll to line number general, and public --- TextEditor.cpp | 59 +++++++++++++++++++++++++------------------------- TextEditor.h | 6 +++-- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 030eef93..85144121 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -36,7 +36,7 @@ TextEditor::TextEditor() , mReadOnly(false) , mWithinRender(false) , mScrollToCursor(false) - , mScrollToStatementMarker(false) + , mShouldScrollToLine(false) , mTextChanged(false) , mColorRangeMin(0) , mColorRangeMax(0) @@ -76,7 +76,7 @@ void TextEditor::SetStatementMarker(int line) if(mCurrentStatement != -1) { - mScrollToStatementMarker = true; + EnsureLineVisible(line); } } @@ -932,11 +932,11 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) mScrollToCursor = false; } - if(mScrollToStatementMarker) + if(mShouldScrollToLine) { - EnsureLineVisible(mCurrentStatement); + EnsureLineVisible(mScrollToLine); ImGui::SetWindowFocus(); - mScrollToStatementMarker = false; + mShouldScrollToLine = false; } if(mBreakpointsModified) @@ -1601,6 +1601,31 @@ bool TextEditor::IsDirty() const return mUndoSaveIndex != mUndoIndex; } +void TextEditor::EnsureLineVisible(int line) +{ + if (!mWithinRender) + { + mShouldScrollToLine = true; + mScrollToLine = std::max(0, std::min(line, static_cast(mLines.size()))); + return; + } + + float scrollY = ImGui::GetScrollY(); + + auto height = ImGui::GetWindowHeight(); + + auto top = 1 + (int)ceil(scrollY / mCharAdvance.y); + auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y); + + auto pos = SanitizeCoordinates({ line, 0 }); + + if (line < top) + ImGui::SetScrollY(std::max(0.0f, (pos.mLine + (height / mCharAdvance.y / 2.0f)) * mCharAdvance.y - height)); + if (line > bottom) + ImGui::SetScrollY(std::max(0.0f, (pos.mLine + (height / mCharAdvance.y / 2.0f)) * mCharAdvance.y - height)); + ImGui::SetScrollX(0); +} + const TextEditor::Palette & TextEditor::GetDarkPalette() { static Palette p = { { @@ -1908,30 +1933,6 @@ void TextEditor::EnsureCursorVisible() ImGui::SetScrollX(std::max(0.0f, (len + cTextStart + 4) * mCharAdvance.x - width)); } -void TextEditor::EnsureLineVisible(int line) -{ - if (!mWithinRender) - { - mScrollToStatementMarker = true; - return; - } - - float scrollY = ImGui::GetScrollY(); - - auto height = ImGui::GetWindowHeight(); - - auto top = 1 + (int)ceil(scrollY / mCharAdvance.y); - auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y); - - auto pos = SanitizeCoordinates({line, 0}); - - if (line < top) - ImGui::SetScrollY(std::max(0.0f, (pos.mLine + (height / mCharAdvance.y / 2.0f)) * mCharAdvance.y - height)); - if (line > bottom) - ImGui::SetScrollY(std::max(0.0f, (pos.mLine + (height / mCharAdvance.y / 2.0f)) * mCharAdvance.y - height)); - ImGui::SetScrollX(0); -} - int TextEditor::GetPageSize() const { auto height = ImGui::GetWindowHeight() - 20.0f; diff --git a/TextEditor.h b/TextEditor.h index 62db7873..09a3e2ac 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -224,6 +224,8 @@ class TextEditor void MarkSaved(); bool IsDirty() const; + void EnsureLineVisible(int line); + static const Palette& GetDarkPalette(); static const Palette& GetLightPalette(); static const Palette& GetRetroBluePalette(); @@ -279,7 +281,6 @@ class TextEditor void ColorizeInternal(); int TextDistanceToLineStart(const Coordinates& aFrom) const; void EnsureCursorVisible(); - void EnsureLineVisible(int line); int GetPageSize() const; int AppendBuffer(std::string& aBuffer, char chr, int aIndex); std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const; @@ -319,7 +320,8 @@ class TextEditor bool mReadOnly; bool mWithinRender; bool mScrollToCursor; - bool mScrollToStatementMarker; + bool mShouldScrollToLine; + int mScrollToLine; bool mTextChanged; int mColorRangeMin, mColorRangeMax; SelectionMode mSelectionMode; From 5ba51c3a8af9be71b715232118ffd525ee317e6f Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Wed, 4 Jul 2018 00:56:41 +0200 Subject: [PATCH 10/26] Fix: text area held was a static variable --- TextEditor.cpp | 8 ++++---- TextEditor.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 85144121..8d0af90d 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -41,6 +41,7 @@ TextEditor::TextEditor() , mColorRangeMin(0) , mColorRangeMax(0) , mSelectionMode(SelectionMode::Normal) + , mTextAreaHeld(false) , mCheckMultilineComments(true) , mCurrentStatement(-1) , mBreakpointsModified(false) @@ -611,7 +612,6 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) } } - static bool textAreaHeld = false; if (ImGui::IsWindowHovered()) { static float lastClick = -1.0f; @@ -664,7 +664,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) lastClick = ImGui::GetTime(); } - textAreaHeld = click || ImGui::IsMouseDown(0); + mTextAreaHeld = click || ImGui::IsMouseDown(0); } else if(MouseOverBreakpoints()) // Check for breakpoint changes { @@ -694,7 +694,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) } // Continue to select text while mouse is held - if (textAreaHeld) + if (mTextAreaHeld) { const float scrollCounterMax = 4.0f; static float scrollVerticalCounter = scrollCounterMax; @@ -702,7 +702,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) // Not holding the button down anymore? if(!ImGui::IsMouseDown(0)) { - textAreaHeld = false; + mTextAreaHeld = false; //Reset counter scrollVerticalCounter = scrollCounterMax; } diff --git a/TextEditor.h b/TextEditor.h index 09a3e2ac..928cb451 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -325,6 +325,7 @@ class TextEditor bool mTextChanged; int mColorRangeMin, mColorRangeMax; SelectionMode mSelectionMode; + bool mTextAreaHeld; Palette mPalette; LanguageDefinition mLanguageDefinition; From be0e232de0d08eae3629e52f9c5084370c69c589 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Wed, 11 Jul 2018 04:14:03 +0200 Subject: [PATCH 11/26] Added MarkDirty method, that forces the file to be dirty untill saved --- TextEditor.cpp | 5 +++++ TextEditor.h | 1 + 2 files changed, 6 insertions(+) diff --git a/TextEditor.cpp b/TextEditor.cpp index 8d0af90d..e4ab797e 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1596,6 +1596,11 @@ void TextEditor::MarkSaved() mUndoSaveIndex = mUndoIndex; } +void TextEditor::MarkDirty() +{ + mUndoSaveIndex = -1; +} + bool TextEditor::IsDirty() const { return mUndoSaveIndex != mUndoIndex; diff --git a/TextEditor.h b/TextEditor.h index 928cb451..ec162095 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -222,6 +222,7 @@ class TextEditor void Undo(int aSteps = 1); void Redo(int aSteps = 1); void MarkSaved(); + void MarkDirty(); bool IsDirty() const; void EnsureLineVisible(int line); From 72c3b8e33663cfc940fb71dc058243f5cc4e0189 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Fri, 20 Jul 2018 19:01:31 +0200 Subject: [PATCH 12/26] breakpoint callback is now a std::function instead of a c-style function pointer --- TextEditor.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TextEditor.h b/TextEditor.h index ec162095..7a20a021 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -9,6 +9,7 @@ #include #include #include "imgui.h" +#include class TextEditor { @@ -125,7 +126,6 @@ class TextEditor typedef std::unordered_set Breakpoints; typedef std::array Palette; typedef char Char; - using BreakpointsModifiedCallback = void(*)(TextEditor*); struct Glyph { @@ -176,7 +176,7 @@ class TextEditor void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; } void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers ; mBreakpointsModified = true; } const Breakpoints& GetBreakpoints() const { return mBreakpoints; } - void SetBreakPointsChangedCallback(BreakpointsModifiedCallback callback) { mBreakpointsModifiedCallback = callback; } + void SetBreakPointsChangedCallback(std::function callback) { mBreakpointsModifiedCallback = std::move(callback); } void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); void SetText(const std::string& aText); @@ -336,7 +336,7 @@ class TextEditor int mCurrentStatement; Breakpoints mBreakpoints; bool mBreakpointsModified; - BreakpointsModifiedCallback mBreakpointsModifiedCallback; + std::function mBreakpointsModifiedCallback; ErrorMarkers mErrorMarkers; ImVec2 mCharAdvance; Coordinates mInteractiveStart, mInteractiveEnd; From 673874cf5820ec6cd65962cbbc753f44518928b8 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Tue, 24 Jul 2018 16:00:41 +0200 Subject: [PATCH 13/26] Initial Lexer implementation --- LuaLexer.cpp | 990 +++++++++++++++++++++++++++++++++++++ LuaLexer.h | 124 +++++ LuaToken.h | 137 ++++++ TextEditor.cpp | 1272 ++++++++++++++++++++++++------------------------ TextEditor.h | 41 +- 5 files changed, 1929 insertions(+), 635 deletions(-) create mode 100644 LuaLexer.cpp create mode 100644 LuaLexer.h create mode 100644 LuaToken.h diff --git a/LuaLexer.cpp b/LuaLexer.cpp new file mode 100644 index 00000000..06ea33a6 --- /dev/null +++ b/LuaLexer.cpp @@ -0,0 +1,990 @@ +#include "LuaLexer.h" + +#include "LuaToken.h" + +void LuaLexer::LexAll() +{ + if (_lines.empty()) + return; + + _line = 0; + _col = 0; + _lines[0].mTokens.clear(); + + char c = GetNext(); + + while(c != '\0') + { + if (IsWhitespace(c)) + { + c = GetNext(); + continue; + } + + switch (c) + { + case '+': + AddToken(LuaToken::TYPE_PLUS); + break; + case '-': + if (PeekNext() == '-') // Comment or long comment + { + GetNext(); + const size_t commentStart = _col - 2; + + auto [longComment, level] = ConsumeBeginLongComment(commentStart); + + if(longComment) + { + if(!ConsumeLongBracket(level)) + return; + } + else + { + // Point at the second '-' + size_t commentEnd = _col - 1; + + // Consume characters (and increment commentEnd) until end of line or end of file + do + { + c = PeekNext(); + if (c == '\n' || c == '\0') + { + AddToken({ LuaToken::TYPE_COMMENT, commentStart , commentEnd }); + break; + } + + ++commentEnd; + + GetNext(); + } while (true); + } + } + else + AddToken(LuaToken::TYPE_MINUS); + break; + case '*': + AddToken(LuaToken::TYPE_MUL); + break; + case '/': + AddToken(LuaToken::TYPE_DIV); + break; + case '%': + AddToken(LuaToken::TYPE_MOD); + break; + case '^': + AddToken(LuaToken::TYPE_EXP); + break; + case '#': + AddToken(LuaToken::TYPE_HASH); + break; + case '=': + if (PeekNext() == '=') + { + GetNext(); + AddToken(LuaToken::TYPE_EQ); + } + else + AddToken(LuaToken::TYPE_ASSIGN); + break; + case '~': + if(PeekNext() == '=') + { + GetNext(); + AddToken(LuaToken::TYPE_NEQ); + } + else // Adding error, but keep parsing so the rest of the code get syntax highlight + AddToken({ LuaToken::TYPE_ERROR_STRAY_TILDE }); + break; + case '<': + if (PeekNext() == '=') + { + GetNext(); + AddToken(LuaToken::TYPE_LE); + } + else + AddToken(LuaToken::TYPE_LT); + break; + case '>': + if (PeekNext() == '=') + { + GetNext(); + AddToken(LuaToken::TYPE_GE); + } + else + AddToken(LuaToken::TYPE_GT); + break; + case '(': + AddToken(LuaToken::TYPE_LEFT_B); + break; + case ')': + AddToken(LuaToken::TYPE_RIGHT_B); + break; + case '{': + AddToken(LuaToken::TYPE_LEFT_CB); + break; + case '}': + AddToken(LuaToken::TYPE_RIGHT_CB); + break; + case '[': + { + const size_t stringStart = _col - 1; + + auto[longString, level] = ConsumeBeginLongString(stringStart); + + if (longString) + { + if (!ConsumeLongBracket(level)) + return; + } + else + { + // ConsumeBeginLongString might have failed to match. Need move _col back + _col = stringStart + 1; + + AddToken(LuaToken::TYPE_LEFT_SB); + } + } + break; + case ']': + AddToken(LuaToken::TYPE_RIGHT_SB); + break; + case ';': + AddToken(LuaToken::TYPE_SEMI_COLON); + break; + case ':': + AddToken(LuaToken::TYPE_COLON); + break; + case ',': + AddToken(LuaToken::TYPE_COMMA); + break; + case '.': + if (PeekNext() == '.') + { + GetNext(); + if (PeekNext() == '.') + { + GetNext(); + AddToken(LuaToken::TYPE_VARARG); + } + else + AddToken(LuaToken::TYPE_CONCAT); + } + else + AddToken(LuaToken::TYPE_DOT); + break; + case '\'': + if(!ConsumeString<'\''>(c)) + return; + break; + case '"': + if (!ConsumeString<'"'>(c)) + return; + break; + default: + if (IsBeginningOfIdentifier(c)) + ConsumeIdentifier(c); + else + AddToken({ LuaToken::TYPE_ERROR_BAD_CHARACTER }); + break; + } + + // TODO Verify that all operation leave the next character alone + c = GetNext(); + } +} + +char LuaLexer::GetNext() +{ + std::vector& glyphs = _lines[_line].mGlyphs; + + // After last character on this line? + if (_col == glyphs.size()) + { + // More lines after this one? + if (_line + 1 < _lines.size()) + { + _col = 0; + ++_line; + _lines[_line].mTokens.clear(); + + return '\n'; + } + + return '\0'; + } + + return glyphs[_col++].mChar; +} + +char LuaLexer::PeekNext() const +{ + std::vector& glyphs = _lines[_line].mGlyphs; + + // After last character on this line? + if (_col == glyphs.size()) + { + // More lines after this one? + if (_line + 1 < _lines.size()) + { + return '\n'; + } + + return '\0'; + } + + return glyphs[_col].mChar; +} + +void LuaLexer::AddToken(LuaToken&& token) const +{ + assert(_line < _lines.size()); + _lines[_line].mTokens.emplace_back(token); +} + +std::tuple LuaLexer::ConsumeBeginLongComment(size_t pos) +{ + if (PeekNext() == '[') + { + GetNext(); + + char c{ PeekNext() }; + if (c == '[') + { + GetNext(); + AddToken({ LuaToken::TYPE_BEGIN_LONG_COMMENT, {0, pos} }); + return { true, 0 }; + } + + if (c == '=') + { + GetNext(); + LuaToken::Level level {1}; + + bool search = true; + while (search) + { + c = PeekNext(); + + switch (c) + { + case '[': + GetNext(); + AddToken({ LuaToken::TYPE_BEGIN_LONG_COMMENT, {level, pos} }); + return { false, level }; + case '=': + GetNext(); + ++level; + break; + default: + search = false; + break; + } + } + } + } + + return { false, 0 }; +} + +// Returns true and level of bracket if a long bracket is detected, false and whatever otherwise +std::tuple LuaLexer::ConsumeBeginLongString(size_t pos) +{ + char c{ PeekNext() }; + if (c == '[') + { + GetNext(); + AddToken({ LuaToken::TYPE_BEGIN_LONG_STRING, {0, pos} }); + return { true, 0 }; + } + + if (c == '=') + { + GetNext(); + LuaToken::Level level {1}; + + bool search = true; + while (search) + { + c = PeekNext(); + + switch (c) + { + case '[': + GetNext(); + AddToken({ LuaToken::TYPE_BEGIN_LONG_STRING, {level, pos} }); + return { false, level }; + case '=': + GetNext(); + ++level; + break; + default: + // We have consumed '=' characters, but this wans't a long string. Caller need to move _col back + search = false; + break; + } + } + } + + return { false, 0 }; +} + +bool LuaLexer::ConsumeLongBracket(LuaToken::Level level) +{ + if(level == 0) + { + char c{ PeekNext() }; + + bool search = true; + while (search) + { + switch (c) + { + case ']': + GetNext(); + + c = PeekNext(); + switch (c) + { + case ']': + AddToken({ LuaToken::TYPE_END_LONG_BRACKET, LuaToken::Bracket(level, _col) }); + GetNext(); + return true; + case '\0': + search = false; + break; + default: + GetNext(); + c = PeekNext(); + break; + } + break; + case '\0': + search = false; + break; + default: + GetNext(); + c = PeekNext(); + break; + } + } + } + else + { + char c{ PeekNext() }; + + bool searchBracket = true; + while (searchBracket) + { + LuaToken::Level currentLevel = 0; + + switch (c) + { + case ']': + { + GetNext(); + c = PeekNext(); + + bool searchEquals = true; + while (searchEquals) + { + switch (c) + { + case ']': + if (level != currentLevel) + { + AddToken({ LuaToken::TYPE_END_LONG_BRACKET, LuaToken::Bracket(level, _col) }); + GetNext(); + return true; + } + + GetNext(); + searchEquals = false; + break; + case '=': + GetNext(); + c = PeekNext(); + ++currentLevel; + if (currentLevel > level) + { + searchEquals = false; + } + break; + case '\0': + searchEquals = searchBracket = false; + break; + default: + GetNext(); + c = PeekNext(); + searchEquals = false; + break; + } + } + } + break; + case '\0': + searchBracket = false; + break; + default: + GetNext(); + c = PeekNext(); + break; + } + } + } + + AddToken({LuaToken::TYPE_ERROR_BRACKET}); + return false; +} + +void LuaLexer::ConsumeIdentifier(char c) +{ + _identifierStart = _col - 1; + + switch (c) + { + case 'a': + GetNext(); + return ConsumeAnd(); + case 'b': + GetNext(); + return ConsumeBreak(); + case 'd': + GetNext(); + return ConsumeDo(); + case 'f': + GetNext(); + return ConsumeFalseForFunction(); + case 'i': + GetNext(); + return ConsumeIfIn(); + case 'l': + GetNext(); + return ConsumeLocal(); + case 'n': + GetNext(); + return ConsumeNilNot(); + case 'o': + GetNext(); + return ConsumeOr(); + case 'r': + GetNext(); + return ConsumeRepeatReturn(); + case 't': + GetNext(); + return ConsumeThenTrue(); + case 'u': + GetNext(); + return ConsumeUntil(); + case 'w': + GetNext(); + return ConsumeWhile(); + default: + return ConsumeName(); + } +} + +void LuaLexer::ConsumeAnd() +{ + char c = PeekNext(); + if(c != 'n') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'd') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if(IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_AND}); +} + +void LuaLexer::ConsumeDo() +{ + char c = PeekNext(); + if (c != 'o') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_DO}); +} + +void LuaLexer::ConsumeBreak() +{ + char c = PeekNext(); + if (c != 'r') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'a') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'k') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_BREAK}); +} + +void LuaLexer::ConsumeFalseForFunction() +{ + char c = PeekNext(); + switch(c) + { + case 'a': + GetNext(); + + c = PeekNext(); + if (c != 'l') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 's') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_FALSE}); + break; + case 'o': + GetNext(); + + c = PeekNext(); + if (c != 'r') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_FOR}); + break; + case 'u': + GetNext(); + + c = PeekNext(); + if (c != 'n') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'c') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 't') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'i') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'o') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'n') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_FUNCTION}); + break; + default: + return ConsumeName(); + } +} + +void LuaLexer::ConsumeIfIn() +{ + char c = PeekNext(); + switch (c) + { + case 'f': + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_IF}); + break; + case 'n': + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_IN}); + break; + default: + return ConsumeName(); + } +} + +void LuaLexer::ConsumeLocal() +{ + char c = PeekNext(); + if (c != 'o') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'c') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'a') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'l') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_LOCAL}); +} + +void LuaLexer::ConsumeEndElseElseif() +{ + char c = PeekNext(); + switch (c) + { + case 'n': + GetNext(); + + c = PeekNext(); + if (c != 'd') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_END}); + break; + case 'l': + GetNext(); + return ConsumeElseElseif(); + default: + return ConsumeName(); + } +} + +void LuaLexer::ConsumeElseElseif() +{ + char c = PeekNext(); + if (c != 's') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (!IsPartOfIdentifier(c)) + { + AddToken({LuaToken::TYPE_ELSE}); + return; + } + + if (c != 'i') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'f') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_ELSEIF}); +} + +void LuaLexer::ConsumeNilNot() +{ + char c = PeekNext(); + switch (c) + { + case 'i': + GetNext(); + + c = PeekNext(); + if (c != 'l') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_NIL}); + break; + case 'o': + GetNext(); + + c = PeekNext(); + if (c != 't') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_NOT}); + break; + default: + return ConsumeName(); + } +} + +void LuaLexer::ConsumeOr() +{ + char c = PeekNext(); + if (c != 'r') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_OR}); +} + +void LuaLexer::ConsumeRepeatReturn() +{ + char c = PeekNext(); + if (c != 'e') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + switch (c) + { + case 'p': + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'a') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 't') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_REPEAT}); + break; + case 't': + GetNext(); + + c = PeekNext(); + if (c != 'u') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'r') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'n') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_RETURN}); + break; + default: + return ConsumeName(); + } +} + +void LuaLexer::ConsumeThenTrue() +{ + char c = PeekNext(); + + switch (c) + { + case 'h': + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'n') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_THEN}); + break; + case 'r': + GetNext(); + + c = PeekNext(); + if (c != 'u') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_TRUE}); + break; + default: + return ConsumeName(); + } +} + +void LuaLexer::ConsumeUntil() +{ + char c = PeekNext(); + if (c != 'n') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 't') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'i') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'l') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_UNTIL}); +} + +void LuaLexer::ConsumeWhile() +{ + char c = PeekNext(); + if (c != 'h') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'i') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'l') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(); + + AddToken({LuaToken::TYPE_WHILE}); +} + +void LuaLexer::ConsumeName() +{ + char c = PeekNext(); + while(IsPartOfIdentifier(c)) + { + GetNext(); + c = PeekNext(); + } + + const size_t identifierEnd = _col - 1; + + AddToken({ LuaToken::TYPE_NAME, { _identifierStart, identifierEnd} }); +} + diff --git a/LuaLexer.h b/LuaLexer.h new file mode 100644 index 00000000..51c59e8a --- /dev/null +++ b/LuaLexer.h @@ -0,0 +1,124 @@ +#pragma once + +#include "TextEditor.h" + +class LuaLexer +{ + TextEditor::Lines& _lines; + size_t _line; + size_t _col; + + size_t _identifierStart; + +public: + LuaLexer(TextEditor::Lines& lines) + : _lines(lines), _line(0), _col(0), _identifierStart(0) + {} + + void LexAll(); + +private: + + char GetNext(); + char PeekNext() const; + + void AddToken(LuaToken&& token) const; + + template + bool ConsumeString(char c) + { + const size_t stringStart = _col + 1; + bool ignoreNext = false; + + bool searchString = true; + while (searchString) + { + c = GetNext(); + + switch (c) + { + case '\n': + case '\0': + AddToken(LuaToken::TYPE_ERROR_STRING); + return false; + case '\\': + ignoreNext = !ignoreNext; + break; + case Delimiter: + if (ignoreNext) + { + ignoreNext = false; + } + else + { + searchString = false; + } + break; + default: + if (ignoreNext) + { + ignoreNext = false; + // TODO Verify escape sequence + } + else + { + // TODO Something? Probably not + } + break; + } + + if (c == '\n' || c == '\0') + { + searchString = false; + } + } + + const size_t stringEnd = _col - 1; + + AddToken({ LuaToken::TYPE_STRING, stringStart, stringEnd }); + + return true; + } + + // Returns true and level of bracket if a long bracket is detected, false and whatever otherwise + std::tuple ConsumeBeginLongComment(size_t pos); + + // Returns true and level of bracket if a long bracket is detected, false and whatever otherwise + std::tuple ConsumeBeginLongString(size_t pos); + + bool ConsumeLongBracket(LuaToken::Level level); + + void ConsumeIdentifier(char c); + void ConsumeAnd(); + void ConsumeDo(); + void ConsumeBreak(); + void ConsumeFalseForFunction(); + void ConsumeIfIn(); + void ConsumeLocal(); + void ConsumeEndElseElseif(); + void ConsumeElseElseif(); + void ConsumeNilNot(); + void ConsumeOr(); + void ConsumeRepeatReturn(); + void ConsumeThenTrue(); + void ConsumeUntil(); + void ConsumeWhile(); + void ConsumeName(); + + static bool IsBeginningOfIdentifier(char c) + { + return c >= 'a' && c <= 'z' || + c >= 'A' && c <= 'Z' || + c == '_'; + } + + static bool IsPartOfIdentifier(char c) + { + return IsBeginningOfIdentifier(c) || c >= '0' && c <= '9'; + } + + static bool IsWhitespace(char c) + { + return c == ' ' || c == '\t' || c == '\n'; + } +}; diff --git a/LuaToken.h b/LuaToken.h new file mode 100644 index 00000000..494ff382 --- /dev/null +++ b/LuaToken.h @@ -0,0 +1,137 @@ +#pragma once + +#include + +struct LuaToken +{ + enum Type + { + TYPE_NAME, + + TYPE_AND, + TYPE_BREAK, + TYPE_DO, + TYPE_ELSE, + TYPE_ELSEIF, + TYPE_END, + TYPE_FALSE, + TYPE_FOR, + TYPE_FUNCTION, + TYPE_IF, + TYPE_IN, + TYPE_LOCAL, + TYPE_NIL, + TYPE_NOT, + TYPE_OR, + TYPE_REPEAT, + TYPE_RETURN, + TYPE_THEN, + TYPE_TRUE, + TYPE_UNTIL, + TYPE_WHILE, + + TYPE_PLUS, + TYPE_MINUS, + TYPE_MUL, + TYPE_DIV, + TYPE_MOD, + TYPE_EXP, + TYPE_HASH, + TYPE_EQ, + TYPE_NEQ, + TYPE_LE, + TYPE_GE, + TYPE_LT, + TYPE_GT, + TYPE_ASSIGN, + TYPE_LEFT_B, + TYPE_RIGHT_B, + TYPE_LEFT_CB, + TYPE_RIGHT_CB, + TYPE_LEFT_SB, + TYPE_RIGHT_SB, + TYPE_SEMI_COLON, + TYPE_COLON, + TYPE_COMMA, + TYPE_DOT, + TYPE_CONCAT, + TYPE_VARARG, + + TYPE_COMMENT, + TYPE_BEGIN_LONG_COMMENT, + TYPE_BEGIN_LONG_STRING, + TYPE_END_LONG_BRACKET, + TYPE_STRING, + + TYPE_ERROR_STRAY_TILDE, + TYPE_ERROR_STRING, + TYPE_ERROR_BRACKET, + TYPE_ERROR_BAD_CHARACTER, + }; + + struct StartEnd + { + StartEnd(size_t start, size_t end) + : _start(start), _end(end) + {} + + size_t _start; + size_t _end; + }; + + struct Level + { + Level(size_t level) + : _level(level) + {} + + Level& operator++() + { + ++_level; + return *this; + } + + bool operator==(const Level& other) const + { + return _level == other._level; + } + + bool operator!=(const Level& other) const + { + return _level != other._level; + } + + bool operator>(const Level& other) const + { + return _level > other._level; + } + + size_t _level; + }; + + struct Bracket + { + Bracket(Level level, size_t pos) + : _level(level), _pos(pos) + {} + + Level _level; + size_t _pos; + }; + + LuaToken(Type type) + : _type(type) + {} + + LuaToken(Type type, size_t start, size_t end) + : _type(type), _data(StartEnd(start, end)) + {} + + LuaToken(Type type, Bracket bracket) + : _type(type), _data(bracket) + {} + + Type _type; + + std::variant _data; +}; \ No newline at end of file diff --git a/TextEditor.cpp b/TextEditor.cpp index e4ab797e..a7f39a64 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -5,6 +5,7 @@ #include #include "TextEditor.h" +#include "LuaLexer.h" #include "imgui_internal.h" static const int cTextStart = 7; @@ -38,34 +39,42 @@ TextEditor::TextEditor() , mScrollToCursor(false) , mShouldScrollToLine(false) , mTextChanged(false) - , mColorRangeMin(0) - , mColorRangeMax(0) + //, mColorRangeMin(0) + //, mColorRangeMax(0) , mSelectionMode(SelectionMode::Normal) , mTextAreaHeld(false) - , mCheckMultilineComments(true) + //, mCheckMultilineComments(true) , mCurrentStatement(-1) , mBreakpointsModified(false) , mBreakpointsModifiedCallback(nullptr) { SetPalette(GetDarkPalette()); - SetLanguageDefinition(LanguageDefinition::HLSL()); mLines.push_back(Line()); + + //mLexer = new LuaLexer(mLines); } TextEditor::~TextEditor() { + //if(mLexer) + // delete mLexer; } -void TextEditor::SetLanguageDefinition(const LanguageDefinition & aLanguageDef) +void TextEditor::ClearLexer() { - 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)); + //mLexer = nullptr; } +//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)); +//} + void TextEditor::SetPalette(const Palette & aValue) { mPalette = aValue; @@ -112,8 +121,8 @@ std::string TextEditor::GetText(const Coordinates & aStart, const Coordinates & prevLineNo = it.mLine; const auto& line = mLines[it.mLine]; - if (!line.empty() && it.mColumn < (int)line.size()) - result.push_back(line[it.mColumn].mChar); + if (!line.mGlyphs.empty() && it.mColumn < (int)line.mGlyphs.size()) + result.push_back(line.mGlyphs[it.mColumn].mChar); } return result; @@ -132,11 +141,11 @@ TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates & aVal if (line >= (int)mLines.size()) { line = (int)mLines.size() - 1; - column = mLines.empty() ? 0 : (int)mLines[line].size(); + column = mLines.empty() ? 0 : (int)mLines[line].mGlyphs.size(); } else { - column = mLines.empty() ? 0 : std::min((int)mLines[line].size(), aValue.mColumn); + column = mLines.empty() ? 0 : std::min((int)mLines[line].mGlyphs.size(), aValue.mColumn); } return Coordinates(line, column); @@ -148,7 +157,7 @@ void TextEditor::Advance(Coordinates & aCoordinates) const { auto& line = mLines[aCoordinates.mLine]; - if (aCoordinates.mColumn + 1 < (int)line.size()) + if (aCoordinates.mColumn + 1 < (int)line.mGlyphs.size()) ++aCoordinates.mColumn; else { @@ -169,15 +178,15 @@ void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEn if (aStart.mLine == aEnd.mLine) { auto& line = mLines[aStart.mLine]; - if (aEnd.mColumn >= (int)line.size()) - line.erase(line.begin() + aStart.mColumn, line.end()); + if (aEnd.mColumn >= (int)line.mGlyphs.size()) + line.mGlyphs.erase(line.mGlyphs.begin() + aStart.mColumn, line.mGlyphs.end()); else - line.erase(line.begin() + aStart.mColumn, line.begin() + aEnd.mColumn); + line.mGlyphs.erase(line.mGlyphs.begin() + aStart.mColumn, line.mGlyphs.begin() + aEnd.mColumn); } else { - auto& firstLine = mLines[aStart.mLine]; - auto& lastLine = mLines[aEnd.mLine]; + auto& firstLine = mLines[aStart.mLine].mGlyphs; + auto& lastLine = mLines[aEnd.mLine].mGlyphs; firstLine.erase(firstLine.begin() + aStart.mColumn, firstLine.end()); lastLine.erase(lastLine.begin(), lastLine.begin() + aEnd.mColumn); @@ -190,6 +199,7 @@ void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEn } mTextChanged = true; + LexAll(); } int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValue) @@ -211,12 +221,12 @@ int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValu { if (aWhere.mLine < mLines.size()) { - if (aWhere.mColumn < (int)mLines[aWhere.mLine].size()) + if (aWhere.mColumn < (int)mLines[aWhere.mLine].mGlyphs.size()) { auto& newLine = InsertLine(aWhere.mLine + 1); auto& line = mLines[aWhere.mLine]; - newLine.insert(newLine.begin(), line.begin() + aWhere.mColumn, line.end()); - line.erase(line.begin() + aWhere.mColumn, line.end()); + newLine.mGlyphs.insert(newLine.mGlyphs.begin(), line.mGlyphs.begin() + aWhere.mColumn, line.mGlyphs.end()); + line.mGlyphs.erase(line.mGlyphs.begin() + aWhere.mColumn, line.mGlyphs.end()); } else { @@ -234,12 +244,13 @@ int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValu else { auto& line = mLines[aWhere.mLine]; - line.insert(line.begin() + aWhere.mColumn, Glyph(chr, PaletteIndex::Default)); + line.mGlyphs.insert(line.mGlyphs.begin() + aWhere.mColumn, Glyph(chr, PaletteIndex::Default)); ++aWhere.mColumn; } chr = *(++aValue); mTextChanged = true; + LexAll(); } return totalLines; @@ -273,9 +284,9 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPositi { auto& line = mLines[lineNo]; auto distance = 0; - while (distance < columnCoord && column < (int)line.size()) + while (distance < columnCoord && column < (int)line.mGlyphs.size()) { - if (line[column].mChar == '\t') + if (line.mGlyphs[column].mChar == '\t') distance = (distance / mTabSize) * mTabSize + mTabSize; else ++distance; @@ -300,13 +311,13 @@ TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates & aFrom) con auto& line = mLines[at.mLine]; - if (at.mColumn >= (int)line.size()) + if (at.mColumn >= (int)line.mGlyphs.size()) return at; - auto cstart = (PaletteIndex)line[at.mColumn].mColorIndex; + auto cstart = (PaletteIndex)line.mGlyphs[at.mColumn].mColorIndex; while (at.mColumn > 0) { - if (cstart != (PaletteIndex)line[at.mColumn - 1].mColorIndex) + if (cstart != (PaletteIndex)line.mGlyphs[at.mColumn - 1].mColorIndex) break; --at.mColumn; } @@ -321,13 +332,13 @@ TextEditor::Coordinates TextEditor::FindWordEnd(const Coordinates & aFrom) const auto& line = mLines[at.mLine]; - if (at.mColumn >= (int)line.size()) + if (at.mColumn >= (int)line.mGlyphs.size()) return at; - auto cstart = (PaletteIndex)line[at.mColumn].mColorIndex; - while (at.mColumn < (int)line.size()) + auto cstart = (PaletteIndex)line.mGlyphs[at.mColumn].mColorIndex; + while (at.mColumn < (int)line.mGlyphs.size()) { - if (cstart != (PaletteIndex)line[at.mColumn].mColorIndex) + if (cstart != (PaletteIndex)line.mGlyphs[at.mColumn].mColorIndex) break; ++at.mColumn; } @@ -340,10 +351,10 @@ bool TextEditor::IsOnWordBoundary(const Coordinates & aAt) const return true; auto& line = mLines[aAt.mLine]; - if (aAt.mColumn >= (int)line.size()) + if (aAt.mColumn >= (int)line.mGlyphs.size()) return true; - return line[aAt.mColumn].mColorIndex != line[aAt.mColumn - 1].mColorIndex; + return line.mGlyphs[aAt.mColumn].mColorIndex != line.mGlyphs[aAt.mColumn - 1].mColorIndex; } void TextEditor::RemoveLine(int aStart, int aEnd) @@ -372,6 +383,7 @@ void TextEditor::RemoveLine(int aStart, int aEnd) mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd); mTextChanged = true; + LexAll(); } void TextEditor::RemoveLine(int aIndex) @@ -400,6 +412,7 @@ void TextEditor::RemoveLine(int aIndex) mLines.erase(mLines.begin() + aIndex); mTextChanged = true; + LexAll(); } TextEditor::Line& TextEditor::InsertLine(int aIndex) @@ -435,7 +448,7 @@ std::string TextEditor::GetWordAt(const Coordinates & aCoords) const std::string r; for (auto it = start; it < end; Advance(it)) - r.push_back(mLines[it.mLine][it.mColumn].mChar); + r.push_back(mLines[it.mLine].mGlyphs[it.mColumn].mChar); return r; } @@ -510,6 +523,12 @@ ImVec2 TextEditor::MouseDistanceOutsideTextArea() const return { x,y }; } +void TextEditor::LexAll() +{ + LuaLexer(mLines).LexAll(); + //mLexer->LexAll(); +} + void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) { mWithinRender = true; @@ -740,7 +759,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) } } - ColorizeInternal(); + //ColorizeInternal(); static std::string buffer; auto contentSize = ImGui::GetWindowContentRegionMax(); @@ -762,10 +781,10 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + mCharAdvance.x * cTextStart, lineStartScreenPos.y); auto& line = mLines[lineNo]; - longest = std::max(cTextStart + TextDistanceToLineStart(Coordinates(lineNo, (int) line.size())), longest); + longest = std::max(cTextStart + TextDistanceToLineStart(Coordinates(lineNo, (int) line.mGlyphs.size())), longest); auto columnNo = 0; Coordinates lineStartCoord(lineNo, 0); - Coordinates lineEndCoord(lineNo, (int)line.size()); + Coordinates lineEndCoord(lineNo, (int)line.mGlyphs.size()); int sstart = -1; int ssend = -1; @@ -870,9 +889,9 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) } appendIndex = 0; - auto prevColor = line.empty() ? PaletteIndex::Default : (line[0].mMultiLineComment ? PaletteIndex::MultiLineComment : line[0].mColorIndex); + auto prevColor = line.mGlyphs.empty() ? PaletteIndex::Default : (line.mGlyphs[0].mMultiLineComment ? PaletteIndex::MultiLineComment : line.mGlyphs[0].mColorIndex); - for (auto& glyph : line) + for (auto& glyph : line.mGlyphs) { auto color = glyph.mMultiLineComment ? PaletteIndex::MultiLineComment : glyph.mColorIndex; @@ -902,7 +921,8 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos())); if (!id.empty()) { - auto it = mLanguageDefinition.mIdentifiers.find(id); + // TODO Fix tooltip + /*auto it = mLanguageDefinition.mIdentifiers.find(id); if (it != mLanguageDefinition.mIdentifiers.end()) { ImGui::BeginTooltip(); @@ -918,7 +938,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) ImGui::TextUnformatted(pi->second.mDeclaration.c_str()); ImGui::EndTooltip(); } - } + }*/ } } @@ -962,6 +982,8 @@ void TextEditor::SetText(const std::string & aText) if (aText.empty()) { mLines.push_back(Line()); + mTextChanged = true; + LexAll(); } else { @@ -973,10 +995,11 @@ void TextEditor::SetText(const std::string & aText) mLines.push_back(Line()); else { - mLines.back().push_back(Glyph(chr, PaletteIndex::Default)); + mLines.back().mGlyphs.emplace_back(chr, PaletteIndex::Default); } mTextChanged = true; + LexAll(); } } @@ -984,7 +1007,7 @@ void TextEditor::SetText(const std::string & aText) mUndoSaveIndex = 0; mUndoBuffer.clear(); - Colorize(); + //Colorize(); } void TextEditor::EnterCharacter(Char aChar) @@ -1014,22 +1037,23 @@ void TextEditor::EnterCharacter(Char aChar) InsertLine(coord.mLine + 1); auto& line = mLines[coord.mLine]; auto& newLine = mLines[coord.mLine + 1]; - newLine.insert(newLine.begin(), line.begin() + coord.mColumn, line.end()); - line.erase(line.begin() + coord.mColumn, line.begin() + line.size()); + newLine.mGlyphs.insert(newLine.mGlyphs.begin(), line.mGlyphs.begin() + coord.mColumn, line.mGlyphs.end()); + line.mGlyphs.erase(line.mGlyphs.begin() + coord.mColumn, line.mGlyphs.begin() + line.mGlyphs.size()); mState.mCursorPosition = Coordinates(coord.mLine + 1, 0); } else { auto& line = mLines[coord.mLine]; - if (mOverwrite && (int)line.size() > coord.mColumn) - line[coord.mColumn] = Glyph(aChar, PaletteIndex::Default); + if (mOverwrite && (int)line.mGlyphs.size() > coord.mColumn) + line.mGlyphs[coord.mColumn] = Glyph(aChar, PaletteIndex::Default); else - line.insert(line.begin() + coord.mColumn, Glyph(aChar, PaletteIndex::Default)); + line.mGlyphs.insert(line.mGlyphs.begin() + coord.mColumn, Glyph(aChar, PaletteIndex::Default)); mState.mCursorPosition = coord; ++mState.mCursorPosition.mColumn; } mTextChanged = true; + LexAll(); u.mAdded = aChar; u.mAddedEnd = GetActualCursorCoordinates(); @@ -1037,7 +1061,7 @@ void TextEditor::EnterCharacter(Char aChar) AddUndo(u); - Colorize(coord.mLine - 1, 3); + //Colorize(coord.mLine - 1, 3); EnsureCursorVisible(); } @@ -1090,7 +1114,7 @@ void TextEditor::SetSelection(const Coordinates & aStart, const Coordinates & aE case TextEditor::SelectionMode::Line: { const auto lineNo = mState.mSelectionEnd.mLine; - const auto lineSize = lineNo < mLines.size() ? mLines[lineNo].size() : 0; + const auto lineSize = lineNo < mLines.size() ? mLines[lineNo].mGlyphs.size() : 0; mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0); mState.mSelectionEnd = Coordinates(lineNo, (int) lineSize); break; @@ -1118,7 +1142,7 @@ void TextEditor::InsertText(const char * aValue) SetSelection(pos, pos); SetCursorPosition(pos); - Colorize(start.mLine - 1, totalLines + 2); + //Colorize(start.mLine - 1, totalLines + 2); } void TextEditor::DeleteSelection() @@ -1132,7 +1156,7 @@ void TextEditor::DeleteSelection() SetSelection(mState.mSelectionStart, mState.mSelectionStart); SetCursorPosition(mState.mSelectionStart); - Colorize(mState.mSelectionStart.mLine, 1); + //Colorize(mState.mSelectionStart.mLine, 1); } void TextEditor::MoveUp(int aAmount, bool aSelect) @@ -1204,7 +1228,7 @@ void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) if (mState.mCursorPosition.mLine > 0) { --mState.mCursorPosition.mLine; - mState.mCursorPosition.mColumn = (int)mLines[mState.mCursorPosition.mLine].size(); + mState.mCursorPosition.mColumn = (int)mLines[mState.mCursorPosition.mLine].mGlyphs.size(); } } else @@ -1245,7 +1269,7 @@ void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) while (aAmount-- > 0) { auto& line = mLines[mState.mCursorPosition.mLine]; - if (mState.mCursorPosition.mColumn >= (int)line.size()) + if (mState.mCursorPosition.mColumn >= (int)line.mGlyphs.size()) { if (mState.mCursorPosition.mLine < (int)mLines.size() - 1) { @@ -1255,7 +1279,7 @@ void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) } else { - mState.mCursorPosition.mColumn = std::max(0, std::min((int)line.size(), mState.mCursorPosition.mColumn + 1)); + mState.mCursorPosition.mColumn = std::max(0, std::min((int)line.mGlyphs.size(), mState.mCursorPosition.mColumn + 1)); if (aWordMode) mState.mCursorPosition = FindWordEnd(mState.mCursorPosition); } @@ -1341,7 +1365,7 @@ void TextEditor::MoveHome(bool aSelect) void TextEditor::MoveEnd(bool aSelect) { auto oldPos = mState.mCursorPosition; - SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, (int)mLines[oldPos.mLine].size())); + SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, (int)mLines[oldPos.mLine].mGlyphs.size())); if (mState.mCursorPosition != oldPos) { @@ -1387,7 +1411,7 @@ void TextEditor::Delete() SetCursorPosition(pos); auto& line = mLines[pos.mLine]; - if (pos.mColumn == (int)line.size()) + if (pos.mColumn == (int)line.mGlyphs.size()) { if (pos.mLine == (int)mLines.size() - 1) return; @@ -1397,21 +1421,22 @@ void TextEditor::Delete() Advance(u.mRemovedEnd); auto& nextLine = mLines[pos.mLine + 1]; - line.insert(line.end(), nextLine.begin(), nextLine.end()); + line.mGlyphs.insert(line.mGlyphs.end(), nextLine.mGlyphs.begin(), nextLine.mGlyphs.end()); RemoveLine(pos.mLine + 1); } else { - u.mRemoved = line[pos.mColumn].mChar; + u.mRemoved = line.mGlyphs[pos.mColumn].mChar; u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); u.mRemovedEnd.mColumn++; - line.erase(line.begin() + pos.mColumn); + line.mGlyphs.erase(line.mGlyphs.begin() + pos.mColumn); } mTextChanged = true; + LexAll(); - Colorize(pos.mLine, 1); + //Colorize(pos.mLine, 1); } u.mAfter = mState; @@ -1453,8 +1478,8 @@ void TextEditor::BackSpace() auto& line = mLines[mState.mCursorPosition.mLine]; auto& prevLine = mLines[mState.mCursorPosition.mLine - 1]; - auto prevSize = (int)prevLine.size(); - prevLine.insert(prevLine.end(), line.begin(), line.end()); + auto prevSize = (int)prevLine.mGlyphs.size(); + prevLine.mGlyphs.insert(prevLine.mGlyphs.end(), line.mGlyphs.begin(), line.mGlyphs.end()); RemoveLine(mState.mCursorPosition.mLine); --mState.mCursorPosition.mLine; mState.mCursorPosition.mColumn = prevSize; @@ -1463,19 +1488,20 @@ void TextEditor::BackSpace() { auto& line = mLines[mState.mCursorPosition.mLine]; - u.mRemoved = line[pos.mColumn - 1].mChar; + u.mRemoved = line.mGlyphs[pos.mColumn - 1].mChar; u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); --u.mRemovedStart.mColumn; --mState.mCursorPosition.mColumn; - if (mState.mCursorPosition.mColumn < (int)line.size()) - line.erase(line.begin() + mState.mCursorPosition.mColumn); + if (mState.mCursorPosition.mColumn < (int)line.mGlyphs.size()) + line.mGlyphs.erase(line.mGlyphs.begin() + mState.mCursorPosition.mColumn); } mTextChanged = true; + LexAll(); EnsureCursorVisible(); - Colorize(mState.mCursorPosition.mLine, 1); + //Colorize(mState.mCursorPosition.mLine, 1); } u.mAfter = mState; @@ -1510,7 +1536,7 @@ void TextEditor::Copy() { std::string str; auto& line = mLines[GetActualCursorCoordinates().mLine]; - for (auto& g : line) + for (auto& g : line.mGlyphs) str.push_back(g.mChar); ImGui::SetClipboardText(str.c_str()); } @@ -1730,178 +1756,178 @@ 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); - mCheckMultilineComments = true; -} - -void TextEditor::ColorizeRange(int aFromLine, int aToLine) -{ - if (mLines.empty() || aFromLine >= aToLine) - return; - - std::string buffer; - int endLine = std::max(0, std::min((int)mLines.size(), aToLine)); - for (int i = aFromLine; i < endLine; ++i) - { - bool preproc = false; - auto& line = mLines[i]; - buffer.clear(); - for (auto g : mLines[i]) - { - buffer.push_back(g.mChar); - g.mColorIndex = PaletteIndex::Default; - } - - std::match_results results; - auto last = buffer.cend(); - for (auto first = buffer.cbegin(); first != last; ++first) - { - for (auto& p : mRegexList) - { - if (std::regex_search(first, last, results, p.first, std::regex_constants::match_continuous)) - { - auto v = *results.begin(); - auto start = v.first - buffer.begin(); - auto end = v.second - buffer.begin(); - auto id = buffer.substr(start, end - start); - auto color = p.second; - if (color == PaletteIndex::Identifier) - { - if (!mLanguageDefinition.mCaseSensitive) - std::transform(id.begin(), id.end(), id.begin(), ::toupper); - - if (!preproc) - { - if (mLanguageDefinition.mKeywords.find(id) != mLanguageDefinition.mKeywords.end()) - color = PaletteIndex::Keyword; - else if (mLanguageDefinition.mIdentifiers.find(id) != mLanguageDefinition.mIdentifiers.end()) - color = PaletteIndex::KnownIdentifier; - else if (mLanguageDefinition.mPreprocIdentifiers.find(id) != mLanguageDefinition.mPreprocIdentifiers.end()) - color = PaletteIndex::PreprocIdentifier; - } - else - { - if (mLanguageDefinition.mPreprocIdentifiers.find(id) != mLanguageDefinition.mPreprocIdentifiers.end()) - color = PaletteIndex::PreprocIdentifier; - else - color = PaletteIndex::Identifier; - } - } - else if (color == PaletteIndex::Preprocessor) - { - preproc = true; - } - for (int j = (int)start; j < (int)end; ++j) - line[j].mColorIndex = color; - first += end - start - 1; - break; - } - } - } - } -} - -void TextEditor::ColorizeInternal() -{ - if (mLines.empty()) - return; - - if (mCheckMultilineComments) - { - auto end = Coordinates((int)mLines.size(), 0); - auto commentStart = end; - auto withinString = false; - for (auto i = Coordinates(0, 0); i < end; Advance(i)) - { - auto& line = mLines[i.mLine]; - if (!line.empty()) - { - auto g = line[i.mColumn]; - auto c = g.mChar; - - bool inComment = commentStart <= i; - - if (withinString) - { - line[i.mColumn].mMultiLineComment = inComment; - - if (c == '\"') - { - if (i.mColumn + 1 < (int)line.size() && line[i.mColumn + 1].mChar == '\"') - { - Advance(i); - if (i.mColumn < (int)line.size()) - line[i.mColumn].mMultiLineComment = inComment; - } - else - withinString = false; - } - else if (c == '\\') - { - Advance(i); - if (i.mColumn < (int)line.size()) - line[i.mColumn].mMultiLineComment = inComment; - } - } - else - { - if (c == '\"') - { - withinString = true; - line[i.mColumn].mMultiLineComment = inComment; - } - else - { - auto pred = [](const char& a, const Glyph& b) { return a == b.mChar; }; - auto from = line.begin() + i.mColumn; - auto& startStr = mLanguageDefinition.mCommentStart; - if (i.mColumn + startStr.size() <= line.size() && - equals(startStr.begin(), startStr.end(), from, from + startStr.size(), pred)) - commentStart = i; - - inComment = commentStart <= i; - - line[i.mColumn].mMultiLineComment = inComment; - - auto& endStr = mLanguageDefinition.mCommentEnd; - if (i.mColumn + 1 >= (int)endStr.size() && - equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(), from + 1, pred)) - commentStart = end; - } - } - } - } - mCheckMultilineComments = false; - return; - } - - if (mColorRangeMin < mColorRangeMax) - { - int to = std::min(mColorRangeMin + 10, mColorRangeMax); - ColorizeRange(mColorRangeMin, to); - mColorRangeMin = to; - - if (mColorRangeMax == mColorRangeMin) - { - mColorRangeMin = std::numeric_limits::max(); - mColorRangeMax = 0; - } - return; - } -} +//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); +// mCheckMultilineComments = true; +//} +// +//void TextEditor::ColorizeRange(int aFromLine, int aToLine) +//{ +// if (mLines.empty() || aFromLine >= aToLine) +// return; +// +// std::string buffer; +// int endLine = std::max(0, std::min((int)mLines.size(), aToLine)); +// for (int i = aFromLine; i < endLine; ++i) +// { +// bool preproc = false; +// auto& line = mLines[i]; +// buffer.clear(); +// for (auto g : mLines[i]) +// { +// buffer.push_back(g.mChar); +// g.mColorIndex = PaletteIndex::Default; +// } +// +// std::match_results results; +// auto last = buffer.cend(); +// for (auto first = buffer.cbegin(); first != last; ++first) +// { +// for (auto& p : mRegexList) +// { +// if (std::regex_search(first, last, results, p.first, std::regex_constants::match_continuous)) +// { +// auto v = *results.begin(); +// auto start = v.first - buffer.begin(); +// auto end = v.second - buffer.begin(); +// auto id = buffer.substr(start, end - start); +// auto color = p.second; +// if (color == PaletteIndex::Identifier) +// { +// if (!mLanguageDefinition.mCaseSensitive) +// std::transform(id.begin(), id.end(), id.begin(), ::toupper); +// +// if (!preproc) +// { +// if (mLanguageDefinition.mKeywords.find(id) != mLanguageDefinition.mKeywords.end()) +// color = PaletteIndex::Keyword; +// else if (mLanguageDefinition.mIdentifiers.find(id) != mLanguageDefinition.mIdentifiers.end()) +// color = PaletteIndex::KnownIdentifier; +// else if (mLanguageDefinition.mPreprocIdentifiers.find(id) != mLanguageDefinition.mPreprocIdentifiers.end()) +// color = PaletteIndex::PreprocIdentifier; +// } +// else +// { +// if (mLanguageDefinition.mPreprocIdentifiers.find(id) != mLanguageDefinition.mPreprocIdentifiers.end()) +// color = PaletteIndex::PreprocIdentifier; +// else +// color = PaletteIndex::Identifier; +// } +// } +// else if (color == PaletteIndex::Preprocessor) +// { +// preproc = true; +// } +// for (int j = (int)start; j < (int)end; ++j) +// line.mGlyphs[j].mColorIndex = color; +// first += end - start - 1; +// break; +// } +// } +// } +// } +//} + +//void TextEditor::ColorizeInternal() +//{ +// if (mLines.empty()) +// return; +// +// if (mCheckMultilineComments) +// { +// auto end = Coordinates((int)mLines.size(), 0); +// auto commentStart = end; +// auto withinString = false; +// for (auto i = Coordinates(0, 0); i < end; Advance(i)) +// { +// auto& line = mLines[i.mLine]; +// if (!line.mGlyphs.empty()) +// { +// auto g = line.mGlyphs[i.mColumn]; +// auto c = g.mChar; +// +// bool inComment = commentStart <= i; +// +// if (withinString) +// { +// line.mGlyphs[i.mColumn].mMultiLineComment = inComment; +// +// if (c == '\"') +// { +// if (i.mColumn + 1 < (int)line.mGlyphs.size() && line.mGlyphs[i.mColumn + 1].mChar == '\"') +// { +// Advance(i); +// if (i.mColumn < (int)line.mGlyphs.size()) +// line.mGlyphs[i.mColumn].mMultiLineComment = inComment; +// } +// else +// withinString = false; +// } +// else if (c == '\\') +// { +// Advance(i); +// if (i.mColumn < (int)line.mGlyphs.size()) +// line.mGlyphs[i.mColumn].mMultiLineComment = inComment; +// } +// } +// else +// { +// if (c == '\"') +// { +// withinString = true; +// line.mGlyphs[i.mColumn].mMultiLineComment = inComment; +// } +// else +// { +// auto pred = [](const char& a, const Glyph& b) { return a == b.mChar; }; +// auto from = line.mGlyphs.begin() + i.mColumn; +// auto& startStr = mLanguageDefinition.mCommentStart; +// if (i.mColumn + startStr.size() <= line.mGlyphs.size() && +// equals(startStr.begin(), startStr.end(), from, from + startStr.size(), pred)) +// commentStart = i; +// +// inComment = commentStart <= i; +// +// line.mGlyphs[i.mColumn].mMultiLineComment = inComment; +// +// auto& endStr = mLanguageDefinition.mCommentEnd; +// if (i.mColumn + 1 >= (int)endStr.size() && +// equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(), from + 1, pred)) +// commentStart = end; +// } +// } +// } +// } +// mCheckMultilineComments = false; +// return; +// } +// +// if (mColorRangeMin < mColorRangeMax) +// { +// int to = std::min(mColorRangeMin + 10, mColorRangeMax); +// ColorizeRange(mColorRangeMin, to); +// mColorRangeMin = to; +// +// if (mColorRangeMax == mColorRangeMin) +// { +// mColorRangeMin = std::numeric_limits::max(); +// mColorRangeMax = 0; +// } +// return; +// } +//} int TextEditor::TextDistanceToLineStart(const Coordinates& aFrom) const { auto& line = mLines[aFrom.mLine]; auto len = 0; - for (size_t it = 0u; it < line.size() && it < (unsigned)aFrom.mColumn; ++it) - len = line[it].mChar == '\t' ? (len / mTabSize) * mTabSize + mTabSize : len + 1; + for (size_t it = 0u; it < line.mGlyphs.size() && it < (unsigned)aFrom.mColumn; ++it) + len = line.mGlyphs[it].mChar == '\t' ? (len / mTabSize) * mTabSize + mTabSize : len + 1; return len; } @@ -1971,14 +1997,14 @@ void TextEditor::UndoRecord::Undo(TextEditor * aEditor) if (!mAdded.empty()) { aEditor->DeleteRange(mAddedStart, mAddedEnd); - aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 2); + //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->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 2); } aEditor->mState = mBefore; @@ -1991,399 +2017,399 @@ void TextEditor::UndoRecord::Redo(TextEditor * aEditor) if (!mRemoved.empty()) { aEditor->DeleteRange(mRemovedStart, mRemovedEnd); - aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 1); + //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->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 1); } aEditor->mState = mAfter; aEditor->EnsureCursorVisible(); } -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.mTokenRegexStrings.push_back(std::make_pair("//.*", PaletteIndex::Comment)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); - langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); - - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - - langDef.mCaseSensitive = true; - - langDef.mName = "C++"; - - inited = true; - } - return langDef; -} - -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("//.*", PaletteIndex::Comment)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); - langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); - - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - - langDef.mCaseSensitive = true; - - langDef.mName = "HLSL"; - - inited = true; - } - return langDef; -} - -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("//.*", PaletteIndex::Comment)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); - langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); - - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - - langDef.mCaseSensitive = true; - - langDef.mName = "GLSL"; - - inited = true; - } - return langDef; -} - -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.mTokenRegexStrings.push_back(std::make_pair("//.*", PaletteIndex::Comment)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); - langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); - - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - - langDef.mCaseSensitive = true; - - langDef.mName = "C"; - - inited = true; - } - return langDef; -} - -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("\\-\\-.*", PaletteIndex::Comment)); - langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\\'[^\\\']*\\\'", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); - - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - - langDef.mCaseSensitive = false; - - langDef.mName = "SQL"; - - inited = true; - } - return langDef; -} - -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("//.*", PaletteIndex::Comment)); - langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); - - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - - langDef.mCaseSensitive = true; - - langDef.mName = "AngelScript"; - - inited = true; - } - return langDef; -} - -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("\\-\\-.*", PaletteIndex::Comment)); - langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\\'[^\\\']*\\\'", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); - - langDef.mCommentStart = "\\-\\-\\[\\["; - langDef.mCommentEnd = "\\]\\]"; - - langDef.mCaseSensitive = true; - - langDef.mName = "Lua"; - - inited = true; - } - return langDef; -} +//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.mTokenRegexStrings.push_back(std::make_pair("//.*", PaletteIndex::Comment)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); +// +// langDef.mCommentStart = "/*"; +// langDef.mCommentEnd = "*/"; +// +// langDef.mCaseSensitive = true; +// +// langDef.mName = "C++"; +// +// inited = true; +// } +// return langDef; +//} +// +//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("//.*", PaletteIndex::Comment)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); +// +// langDef.mCommentStart = "/*"; +// langDef.mCommentEnd = "*/"; +// +// langDef.mCaseSensitive = true; +// +// langDef.mName = "HLSL"; +// +// inited = true; +// } +// return langDef; +//} +// +//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("//.*", PaletteIndex::Comment)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); +// +// langDef.mCommentStart = "/*"; +// langDef.mCommentEnd = "*/"; +// +// langDef.mCaseSensitive = true; +// +// langDef.mName = "GLSL"; +// +// inited = true; +// } +// return langDef; +//} +// +//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.mTokenRegexStrings.push_back(std::make_pair("//.*", PaletteIndex::Comment)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); +// +// langDef.mCommentStart = "/*"; +// langDef.mCommentEnd = "*/"; +// +// langDef.mCaseSensitive = true; +// +// langDef.mName = "C"; +// +// inited = true; +// } +// return langDef; +//} +// +//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("\\-\\-.*", PaletteIndex::Comment)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("\\\'[^\\\']*\\\'", PaletteIndex::String)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); +// +// langDef.mCommentStart = "/*"; +// langDef.mCommentEnd = "*/"; +// +// langDef.mCaseSensitive = false; +// +// langDef.mName = "SQL"; +// +// inited = true; +// } +// return langDef; +//} +// +//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("//.*", PaletteIndex::Comment)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::String)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); +// +// langDef.mCommentStart = "/*"; +// langDef.mCommentEnd = "*/"; +// +// langDef.mCaseSensitive = true; +// +// langDef.mName = "AngelScript"; +// +// inited = true; +// } +// return langDef; +//} +// +//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("\\-\\-.*", PaletteIndex::Comment)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("\\\'[^\\\']*\\\'", PaletteIndex::String)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); +// langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); +// +// langDef.mCommentStart = "\\-\\-\\[\\["; +// langDef.mCommentEnd = "\\]\\]"; +// +// langDef.mCaseSensitive = true; +// +// langDef.mName = "Lua"; +// +// inited = true; +// } +// return langDef; +//} diff --git a/TextEditor.h b/TextEditor.h index 7a20a021..15b2908c 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -10,6 +10,10 @@ #include #include "imgui.h" #include +#include "LuaToken.h" + + +class LuaLexer; class TextEditor { @@ -136,10 +140,15 @@ class TextEditor Glyph(Char aChar, PaletteIndex aColorIndex) : mChar(aChar), mColorIndex(aColorIndex), mMultiLineComment(false) {} }; - typedef std::vector Line; + struct Line + { + std::vector mGlyphs; + std::vector mTokens; + }; + typedef std::vector Lines; - struct LanguageDefinition + /*struct LanguageDefinition { typedef std::pair TokenRegexString; typedef std::vector TokenRegexStrings; @@ -161,13 +170,15 @@ class TextEditor static LanguageDefinition SQL(); static LanguageDefinition AngelScript(); static LanguageDefinition Lua(); - }; + };*/ TextEditor(); ~TextEditor(); - void SetLanguageDefinition(const LanguageDefinition& aLanguageDef); - const LanguageDefinition& GetLanguageDefinition() const { return mLanguageDefinition; } + void ClearLexer(); + + /*void SetLanguageDefinition(const LanguageDefinition& aLanguageDef); + const LanguageDefinition& GetLanguageDefinition() const { return mLanguageDefinition; }*/ const Palette& GetPalette() const { return mPalette; } void SetPalette(const Palette& aValue); @@ -277,9 +288,9 @@ class TextEditor typedef std::vector UndoBuffer; void ProcessInputs(); - void Colorize(int aFromLine = 0, int aCount = -1); - void ColorizeRange(int aFromLine = 0, int aToLine = 0); - void ColorizeInternal(); + //void Colorize(int aFromLine = 0, int aCount = -1); + //void ColorizeRange(int aFromLine = 0, int aToLine = 0); + //void ColorizeInternal(); int TextDistanceToLineStart(const Coordinates& aFrom) const; void EnsureCursorVisible(); int GetPageSize() const; @@ -309,6 +320,10 @@ class TextEditor bool MouseOverBreakpoints() const; ImVec2 MouseDistanceOutsideTextArea() const; + void LexAll(); + + + float mLineSpacing; Lines mLines; EditorState mState; @@ -324,15 +339,15 @@ class TextEditor bool mShouldScrollToLine; int mScrollToLine; bool mTextChanged; - int mColorRangeMin, mColorRangeMax; + //int mColorRangeMin, mColorRangeMax; SelectionMode mSelectionMode; bool mTextAreaHeld; Palette mPalette; - LanguageDefinition mLanguageDefinition; - RegexList mRegexList; + /*LanguageDefinition mLanguageDefinition;*/ + //RegexList mRegexList; - bool mCheckMultilineComments; + //bool mCheckMultilineComments; int mCurrentStatement; Breakpoints mBreakpoints; bool mBreakpointsModified; @@ -340,5 +355,7 @@ class TextEditor ErrorMarkers mErrorMarkers; ImVec2 mCharAdvance; Coordinates mInteractiveStart, mInteractiveEnd; + + //LuaLexer* mLexer; }; From f874cac5c37b02fc096135c6e68a6ba85bd7353a Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Wed, 25 Jul 2018 00:58:24 +0200 Subject: [PATCH 14/26] Fixed some off by one errors. Fixed missing check for keywords starting with 'e' Lexer now colors the code as it runns. Now lexing numbers aswell --- LuaLexer.cpp | 439 ++++++++++++++++++++++++++++++++++++++----------- LuaLexer.h | 15 +- LuaToken.h | 2 + TextEditor.cpp | 69 +++++--- TextEditor.h | 8 +- 5 files changed, 405 insertions(+), 128 deletions(-) diff --git a/LuaLexer.cpp b/LuaLexer.cpp index 06ea33a6..3f5029bb 100644 --- a/LuaLexer.cpp +++ b/LuaLexer.cpp @@ -1,6 +1,7 @@ #include "LuaLexer.h" #include "LuaToken.h" +#include void LuaLexer::LexAll() { @@ -25,23 +26,27 @@ void LuaLexer::LexAll() { case '+': AddToken(LuaToken::TYPE_PLUS); + ColorCurrent(TextEditor::PaletteIndex::Default); break; case '-': + if (PeekNext() == '-') // Comment or long comment { + ColorCurrent(TextEditor::PaletteIndex::Comment); GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Comment); const size_t commentStart = _col - 2; auto [longComment, level] = ConsumeBeginLongComment(commentStart); if(longComment) { - if(!ConsumeLongBracket(level)) + if(!ConsumeLongBracket(level, TextEditor::PaletteIndex::Comment)) return; } else { - // Point at the second '-' + // Point at the second '-' or at the point ConsumeBeginLongComment have placed us size_t commentEnd = _col - 1; // Consume characters (and increment commentEnd) until end of line or end of file @@ -57,31 +62,42 @@ void LuaLexer::LexAll() ++commentEnd; GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Comment); } while (true); } } else + { + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_MINUS); + } break; case '*': + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_MUL); break; case '/': + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_DIV); break; case '%': + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_MOD); break; case '^': + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_EXP); break; case '#': + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_HASH); break; case '=': + ColorCurrent(TextEditor::PaletteIndex::Default); if (PeekNext() == '=') { GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_EQ); } else @@ -90,40 +106,53 @@ void LuaLexer::LexAll() case '~': if(PeekNext() == '=') { + ColorCurrent(TextEditor::PaletteIndex::Default); GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_NEQ); } else // Adding error, but keep parsing so the rest of the code get syntax highlight + { + ColorCurrent(TextEditor::PaletteIndex::ErrorMarker); AddToken({ LuaToken::TYPE_ERROR_STRAY_TILDE }); + } break; case '<': + ColorCurrent(TextEditor::PaletteIndex::Default); if (PeekNext() == '=') { GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_LE); } - else + else AddToken(LuaToken::TYPE_LT); break; case '>': + ColorCurrent(TextEditor::PaletteIndex::Default); if (PeekNext() == '=') { GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_GE); } else AddToken(LuaToken::TYPE_GT); break; case '(': + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_LEFT_B); break; case ')': + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_RIGHT_B); break; case '{': + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_LEFT_CB); break; case '}': + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_RIGHT_CB); break; case '[': @@ -134,7 +163,7 @@ void LuaLexer::LexAll() if (longString) { - if (!ConsumeLongBracket(level)) + if (!ConsumeLongBracket(level, TextEditor::PaletteIndex::String)) return; } else @@ -142,28 +171,36 @@ void LuaLexer::LexAll() // ConsumeBeginLongString might have failed to match. Need move _col back _col = stringStart + 1; + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_LEFT_SB); } } break; case ']': + ColorCurrent(TextEditor::PaletteIndex::Default); AddToken(LuaToken::TYPE_RIGHT_SB); break; case ';': AddToken(LuaToken::TYPE_SEMI_COLON); + ColorCurrent(TextEditor::PaletteIndex::Punctuation); break; case ':': AddToken(LuaToken::TYPE_COLON); + ColorCurrent(TextEditor::PaletteIndex::Punctuation); break; case ',': AddToken(LuaToken::TYPE_COMMA); + ColorCurrent(TextEditor::PaletteIndex::Punctuation); break; case '.': - if (PeekNext() == '.') + c = PeekNext(); + if (c == '.') { + ColorCurrent(TextEditor::PaletteIndex::Punctuation); GetNext(); if (PeekNext() == '.') { + ColorCurrent(TextEditor::PaletteIndex::Punctuation); GetNext(); AddToken(LuaToken::TYPE_VARARG); } @@ -171,31 +208,60 @@ void LuaLexer::LexAll() AddToken(LuaToken::TYPE_CONCAT); } else + { + if(IsBeginningOfIdentifier(c)) + break; + + if (isdigit(c)) + { + _identifierStart = _col - 1; + ConsumeDotNumber(c); + break; + } + + ColorCurrent(TextEditor::PaletteIndex::Punctuation); AddToken(LuaToken::TYPE_DOT); + } break; case '\'': + ColorCurrent(TextEditor::PaletteIndex::String); if(!ConsumeString<'\''>(c)) return; break; case '"': + ColorCurrent(TextEditor::PaletteIndex::String); if (!ConsumeString<'"'>(c)) return; break; default: + + // Start for ConsumeNumber + _identifierStart = _col - 1; + if (IsBeginningOfIdentifier(c)) + { ConsumeIdentifier(c); - else - AddToken({ LuaToken::TYPE_ERROR_BAD_CHARACTER }); + break; + } + + if(isdigit(c)) + { + ConsumeNumber(c); + break; + } + + ColorCurrent(TextEditor::PaletteIndex::ErrorMarker); + AddToken({ LuaToken::TYPE_ERROR_BAD_CHARACTER }); break; } - // TODO Verify that all operation leave the next character alone c = GetNext(); } } char LuaLexer::GetNext() { + //std::cout << "GetNext: \t" << _line << ", " << _col << " ; "; std::vector& glyphs = _lines[_line].mGlyphs; // After last character on this line? @@ -208,12 +274,16 @@ char LuaLexer::GetNext() ++_line; _lines[_line].mTokens.clear(); + //std::cout << _line << ", " << _col << " : \\n\n"; return '\n'; } + //std::cout << _line << ", " << _col << " : \\0\n"; return '\0'; } + //std::cout << _line << ", " << _col + 1 << " : " << glyphs[_col].mChar << '\n'; + glyphs[_col].mColorIndex = TextEditor::PaletteIndex::Default; return glyphs[_col++].mChar; } @@ -227,15 +297,37 @@ char LuaLexer::PeekNext() const // More lines after this one? if (_line + 1 < _lines.size()) { + //std::cout << "PeekNext: \t" << _line << ", " << _col << " : \\n\n"; return '\n'; } + //std::cout << "PeekNext: \t" << _line << ", " << _col << " : \\0\n"; return '\0'; } + //std::cout << "PeekNext: \t" << _line << ", " << _col << " : " << glyphs[_col].mChar << '\n'; return glyphs[_col].mChar; } + +void LuaLexer::ColorCurrent(TextEditor::PaletteIndex color) const +{ + // Current is a newline + if(_col == 0) + return; + + _lines[_line].mGlyphs[_col - 1].mColorIndex = color; +} + +void LuaLexer::ColorRange(size_t begin, size_t end, TextEditor::PaletteIndex color) const +{ + auto& glyphs = _lines[_line].mGlyphs; + for(size_t i = begin; i <= end; ++i) + { + glyphs[i].mColorIndex = color; + } +} + void LuaLexer::AddToken(LuaToken&& token) const { assert(_line < _lines.size()); @@ -247,11 +339,13 @@ std::tuple LuaLexer::ConsumeBeginLongComment(size_t pos) if (PeekNext() == '[') { GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Comment); char c{ PeekNext() }; if (c == '[') { GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Comment); AddToken({ LuaToken::TYPE_BEGIN_LONG_COMMENT, {0, pos} }); return { true, 0 }; } @@ -259,6 +353,7 @@ std::tuple LuaLexer::ConsumeBeginLongComment(size_t pos) if (c == '=') { GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Comment); LuaToken::Level level {1}; bool search = true; @@ -270,10 +365,12 @@ std::tuple LuaLexer::ConsumeBeginLongComment(size_t pos) { case '[': GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Comment); AddToken({ LuaToken::TYPE_BEGIN_LONG_COMMENT, {level, pos} }); return { false, level }; case '=': GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Comment); ++level; break; default: @@ -293,13 +390,16 @@ std::tuple LuaLexer::ConsumeBeginLongString(size_t pos) char c{ PeekNext() }; if (c == '[') { + ColorCurrent(TextEditor::PaletteIndex::String); GetNext(); + ColorCurrent(TextEditor::PaletteIndex::String); AddToken({ LuaToken::TYPE_BEGIN_LONG_STRING, {0, pos} }); return { true, 0 }; } if (c == '=') { + ColorCurrent(TextEditor::PaletteIndex::String); GetNext(); LuaToken::Level level {1}; @@ -311,10 +411,13 @@ std::tuple LuaLexer::ConsumeBeginLongString(size_t pos) switch (c) { case '[': + ColorCurrent(TextEditor::PaletteIndex::String); GetNext(); + ColorCurrent(TextEditor::PaletteIndex::String); AddToken({ LuaToken::TYPE_BEGIN_LONG_STRING, {level, pos} }); - return { false, level }; + return { true, level }; case '=': + ColorCurrent(TextEditor::PaletteIndex::String); GetNext(); ++level; break; @@ -329,7 +432,7 @@ std::tuple LuaLexer::ConsumeBeginLongString(size_t pos) return { false, 0 }; } -bool LuaLexer::ConsumeLongBracket(LuaToken::Level level) +bool LuaLexer::ConsumeLongBracket(LuaToken::Level level, TextEditor::PaletteIndex color) { if(level == 0) { @@ -342,6 +445,7 @@ bool LuaLexer::ConsumeLongBracket(LuaToken::Level level) { case ']': GetNext(); + ColorCurrent(color); c = PeekNext(); switch (c) @@ -349,12 +453,14 @@ bool LuaLexer::ConsumeLongBracket(LuaToken::Level level) case ']': AddToken({ LuaToken::TYPE_END_LONG_BRACKET, LuaToken::Bracket(level, _col) }); GetNext(); + ColorCurrent(color); return true; case '\0': search = false; break; default: GetNext(); + ColorCurrent(color); c = PeekNext(); break; } @@ -364,6 +470,7 @@ bool LuaLexer::ConsumeLongBracket(LuaToken::Level level) break; default: GetNext(); + ColorCurrent(color); c = PeekNext(); break; } @@ -383,6 +490,7 @@ bool LuaLexer::ConsumeLongBracket(LuaToken::Level level) case ']': { GetNext(); + ColorCurrent(color); c = PeekNext(); bool searchEquals = true; @@ -391,18 +499,21 @@ bool LuaLexer::ConsumeLongBracket(LuaToken::Level level) switch (c) { case ']': - if (level != currentLevel) + if (level == currentLevel) { AddToken({ LuaToken::TYPE_END_LONG_BRACKET, LuaToken::Bracket(level, _col) }); GetNext(); + ColorCurrent(color); return true; } GetNext(); + ColorCurrent(color); searchEquals = false; break; case '=': GetNext(); + ColorCurrent(color); c = PeekNext(); ++currentLevel; if (currentLevel > level) @@ -415,6 +526,7 @@ bool LuaLexer::ConsumeLongBracket(LuaToken::Level level) break; default: GetNext(); + ColorCurrent(color); c = PeekNext(); searchEquals = false; break; @@ -427,6 +539,7 @@ bool LuaLexer::ConsumeLongBracket(LuaToken::Level level) break; default: GetNext(); + ColorCurrent(color); c = PeekNext(); break; } @@ -444,43 +557,33 @@ void LuaLexer::ConsumeIdentifier(char c) switch (c) { case 'a': - GetNext(); return ConsumeAnd(); case 'b': - GetNext(); return ConsumeBreak(); case 'd': - GetNext(); return ConsumeDo(); + case 'e': + return ConsumeEndElseElseif(); case 'f': - GetNext(); return ConsumeFalseForFunction(); case 'i': - GetNext(); return ConsumeIfIn(); case 'l': - GetNext(); return ConsumeLocal(); case 'n': - GetNext(); return ConsumeNilNot(); case 'o': - GetNext(); return ConsumeOr(); case 'r': - GetNext(); return ConsumeRepeatReturn(); case 't': - GetNext(); return ConsumeThenTrue(); case 'u': - GetNext(); return ConsumeUntil(); case 'w': - GetNext(); return ConsumeWhile(); default: - return ConsumeName(); + return ConsumeName(PeekNext()); } } @@ -488,18 +591,19 @@ void LuaLexer::ConsumeAnd() { char c = PeekNext(); if(c != 'n') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'd') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if(IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_AND}); } @@ -507,13 +611,14 @@ void LuaLexer::ConsumeDo() { char c = PeekNext(); if (c != 'o') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_DO}); } @@ -521,28 +626,29 @@ void LuaLexer::ConsumeBreak() { char c = PeekNext(); if (c != 'r') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'e') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'a') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'k') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_BREAK}); } @@ -556,23 +662,24 @@ void LuaLexer::ConsumeFalseForFunction() c = PeekNext(); if (c != 'l') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 's') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'e') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_FALSE}); break; case 'o': @@ -580,13 +687,14 @@ void LuaLexer::ConsumeFalseForFunction() c = PeekNext(); if (c != 'r') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_FOR}); break; case 'u': @@ -594,42 +702,43 @@ void LuaLexer::ConsumeFalseForFunction() c = PeekNext(); if (c != 'n') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'c') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 't') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'i') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'o') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'n') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_FUNCTION}); break; default: - return ConsumeName(); + return ConsumeName(c); } } @@ -643,8 +752,9 @@ void LuaLexer::ConsumeIfIn() c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_IF}); break; case 'n': @@ -652,12 +762,13 @@ void LuaLexer::ConsumeIfIn() c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_IN}); break; default: - return ConsumeName(); + return ConsumeName(c); } } @@ -665,28 +776,29 @@ void LuaLexer::ConsumeLocal() { char c = PeekNext(); if (c != 'o') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'c') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'a') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'l') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_LOCAL}); } @@ -700,20 +812,21 @@ void LuaLexer::ConsumeEndElseElseif() c = PeekNext(); if (c != 'd') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_END}); break; case 'l': GetNext(); return ConsumeElseElseif(); default: - return ConsumeName(); + return ConsumeName(c); } } @@ -721,34 +834,36 @@ void LuaLexer::ConsumeElseElseif() { char c = PeekNext(); if (c != 's') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'e') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (!IsPartOfIdentifier(c)) { + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_ELSE}); return; } if (c != 'i') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'f') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_ELSEIF}); } @@ -762,13 +877,14 @@ void LuaLexer::ConsumeNilNot() c = PeekNext(); if (c != 'l') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_NIL}); break; case 'o': @@ -776,17 +892,18 @@ void LuaLexer::ConsumeNilNot() c = PeekNext(); if (c != 't') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_NOT}); break; default: - return ConsumeName(); + return ConsumeName(c); } } @@ -794,13 +911,14 @@ void LuaLexer::ConsumeOr() { char c = PeekNext(); if (c != 'r') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_OR}); } @@ -808,7 +926,7 @@ void LuaLexer::ConsumeRepeatReturn() { char c = PeekNext(); if (c != 'e') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); @@ -819,23 +937,24 @@ void LuaLexer::ConsumeRepeatReturn() c = PeekNext(); if (c != 'e') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'a') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 't') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_REPEAT}); break; case 't': @@ -843,27 +962,28 @@ void LuaLexer::ConsumeRepeatReturn() c = PeekNext(); if (c != 'u') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'r') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'n') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_RETURN}); break; default: - return ConsumeName(); + return ConsumeName(c); } } @@ -878,18 +998,19 @@ void LuaLexer::ConsumeThenTrue() c = PeekNext(); if (c != 'e') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'n') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_THEN}); break; case 'r': @@ -897,22 +1018,23 @@ void LuaLexer::ConsumeThenTrue() c = PeekNext(); if (c != 'u') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'e') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_TRUE}); break; default: - return ConsumeName(); + return ConsumeName(c); } } @@ -920,28 +1042,29 @@ void LuaLexer::ConsumeUntil() { char c = PeekNext(); if (c != 'n') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 't') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'i') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'l') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_UNTIL}); } @@ -949,34 +1072,34 @@ void LuaLexer::ConsumeWhile() { char c = PeekNext(); if (c != 'h') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'i') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'l') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (c != 'e') - return ConsumeName(); + return ConsumeName(c); GetNext(); c = PeekNext(); if (IsPartOfIdentifier(c)) - return ConsumeName(); + return ConsumeName(c); + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); AddToken({LuaToken::TYPE_WHILE}); } -void LuaLexer::ConsumeName() +void LuaLexer::ConsumeName(char c) { - char c = PeekNext(); while(IsPartOfIdentifier(c)) { GetNext(); @@ -985,6 +1108,122 @@ void LuaLexer::ConsumeName() const size_t identifierEnd = _col - 1; + ColorRange(_identifierStart, identifierEnd, TextEditor::PaletteIndex::Identifier); AddToken({ LuaToken::TYPE_NAME, { _identifierStart, identifierEnd} }); } +void LuaLexer::ConsumeNumber(char c) +{ + const char first = c; + c = PeekNext(); + + // Is hex number + if(first == '0' && (c == 'x' || c == 'X')) + { + GetNext(); + return ConsumeHexNumber(PeekNext()); + } + + // Consume optional digits + while (isdigit(c)) + { + GetNext(); + c = PeekNext(); + } + + // Optional dot + if(c == '.') + { + return ConsumeDotNumber(c); + } + + // TODO identifier characters here cause: malformed number near .n{n}e[-]{n}c + // TODO What other characters cause this??? Can end with == ~= or newline or similar + if (IsBeginningOfIdentifier(c)) + { + AddToken({ LuaToken::TYPE_ERROR_MALFORMED_NUMBER, _identifierStart, _col }); + return; + } + + const size_t end = _col - 1; + ColorRange(_identifierStart, end, TextEditor::PaletteIndex::Number); + AddToken({ LuaToken::TYPE_NUMBER, _identifierStart, end }); +} + + +void LuaLexer::ConsumeDotNumber(char c) +{ + GetNext(); + + // Consume optional digits + c = PeekNext(); + while(isdigit(c)) + { + GetNext(); + c = PeekNext(); + } + + if(c == 'e' || c == 'E') + { + GetNext(); + c = PeekNext(); + + // Optional - + if (c == '-') + { + GetNext(); + c = PeekNext(); + } + + // Atleast one digit + if (!isdigit(c)) + { + AddToken({ LuaToken::TYPE_ERROR_MALFORMED_NUMBER, _identifierStart, _col }); + return; + } + + GetNext(); + + // Consume all following digits + c = PeekNext(); + while (isdigit(c)) + { + GetNext(); + c = PeekNext(); + } + } + + // TODO identifier characters here cause: malformed number near .n{n}e[-]{n}c + // TODO What other characters cause this??? Can end with == ~= or newline or similar + if(IsBeginningOfIdentifier(c)) + { + AddToken({ LuaToken::TYPE_ERROR_MALFORMED_NUMBER, _identifierStart, _col}); + return; + } + + const size_t end = _col - 1; + ColorRange(_identifierStart, end, TextEditor::PaletteIndex::Number); + AddToken({LuaToken::TYPE_NUMBER, _identifierStart, end}); +} + +void LuaLexer::ConsumeHexNumber(char c) +{ + while(isxdigit(c)) + { + GetNext(); + c = PeekNext(); + } + + // TODO identifier characters here cause: malformed number near 0x{nx} + // TODO What other characters cause this??? Can end with == ~= or newline or similar + if (IsBeginningOfIdentifier(c)) + { + AddToken({ LuaToken::TYPE_ERROR_MALFORMED_NUMBER, _identifierStart, _col }); + return; + } + + const size_t end = _col - 1; + ColorRange(_identifierStart, end, TextEditor::PaletteIndex::Number); + AddToken({ LuaToken::TYPE_NUMBER, _identifierStart, end }); + return; +} diff --git a/LuaLexer.h b/LuaLexer.h index 51c59e8a..445f2ad8 100644 --- a/LuaLexer.h +++ b/LuaLexer.h @@ -22,6 +22,9 @@ class LuaLexer char GetNext(); char PeekNext() const; + void ColorCurrent(TextEditor::PaletteIndex color) const; + void ColorRange(size_t begin, size_t end, TextEditor::PaletteIndex color) const; + void AddToken(LuaToken&& token) const; template @@ -34,6 +37,7 @@ class LuaLexer while (searchString) { c = GetNext(); + ColorCurrent(TextEditor::PaletteIndex::String); switch (c) { @@ -86,7 +90,7 @@ class LuaLexer // Returns true and level of bracket if a long bracket is detected, false and whatever otherwise std::tuple ConsumeBeginLongString(size_t pos); - bool ConsumeLongBracket(LuaToken::Level level); + bool ConsumeLongBracket(LuaToken::Level level, TextEditor::PaletteIndex color); void ConsumeIdentifier(char c); void ConsumeAnd(); @@ -103,7 +107,14 @@ class LuaLexer void ConsumeThenTrue(); void ConsumeUntil(); void ConsumeWhile(); - void ConsumeName(); + void ConsumeName(char c); + + // c is a number from GetNext + void ConsumeNumber(char c); + // c is the first character peeked after dot + void ConsumeDotNumber(char c); + // c is first character peeked after x + void ConsumeHexNumber(char c); static bool IsBeginningOfIdentifier(char c) { diff --git a/LuaToken.h b/LuaToken.h index 494ff382..c61b348d 100644 --- a/LuaToken.h +++ b/LuaToken.h @@ -62,11 +62,13 @@ struct LuaToken TYPE_BEGIN_LONG_STRING, TYPE_END_LONG_BRACKET, TYPE_STRING, + TYPE_NUMBER, TYPE_ERROR_STRAY_TILDE, TYPE_ERROR_STRING, TYPE_ERROR_BRACKET, TYPE_ERROR_BAD_CHARACTER, + TYPE_ERROR_MALFORMED_NUMBER, }; struct StartEnd diff --git a/TextEditor.cpp b/TextEditor.cpp index a7f39a64..1f3045bd 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -57,13 +57,7 @@ TextEditor::TextEditor() TextEditor::~TextEditor() { - //if(mLexer) - // delete mLexer; -} -void TextEditor::ClearLexer() -{ - //mLexer = nullptr; } //void TextEditor::SetLanguageDefinition(const LanguageDefinition & aLanguageDef) @@ -199,7 +193,6 @@ void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEn } mTextChanged = true; - LexAll(); } int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValue) @@ -250,7 +243,6 @@ int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValu chr = *(++aValue); mTextChanged = true; - LexAll(); } return totalLines; @@ -383,7 +375,6 @@ void TextEditor::RemoveLine(int aStart, int aEnd) mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd); mTextChanged = true; - LexAll(); } void TextEditor::RemoveLine(int aIndex) @@ -412,7 +403,6 @@ void TextEditor::RemoveLine(int aIndex) mLines.erase(mLines.begin() + aIndex); mTextChanged = true; - LexAll(); } TextEditor::Line& TextEditor::InsertLine(int aIndex) @@ -467,6 +457,21 @@ bool TextEditor::MouseOverText() const return ImRect(areaA, areaB).Contains(mousePos); } +bool TextEditor::MouseOverLineNumbers() const +{ + const auto mousePos = ImGui::GetMousePos(); + const auto cursorScreenPos = ImGui::GetCursorScreenPos(); + const auto contentSize = ImGui::GetContentRegionMax(); + const auto scrollY = ImGui::GetScrollY(); + const float breakpoinBarWidth = mCharAdvance.y; + + auto breakAndLineNumberWidth = mCharAdvance.x * cTextStart; + auto lineBarA = ImVec2(cursorScreenPos.x + breakpoinBarWidth, cursorScreenPos.y + scrollY); + auto lineBarB = ImVec2(lineBarA.x + breakAndLineNumberWidth - breakpoinBarWidth, lineBarA.y + contentSize.y + scrollY); + + return ImRect(lineBarA, lineBarB).Contains(mousePos); +} + bool TextEditor::MouseOverBreakpoints() const { const auto mousePos = ImGui::GetMousePos(); @@ -526,12 +531,15 @@ ImVec2 TextEditor::MouseDistanceOutsideTextArea() const void TextEditor::LexAll() { LuaLexer(mLines).LexAll(); - //mLexer->LexAll(); } void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) { mWithinRender = true; + + if(mTextChanged) + LexAll(); + mTextChanged = false; ImGuiIO& io = ImGui::GetIO(); @@ -645,7 +653,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) auto tripleClick = click && !doubleClick && t - lastClick < io.MouseDoubleClickTime; if (tripleClick) { - printf("triple\n"); + //printf("triple\n"); if (!ctrl) { mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); @@ -657,7 +665,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) } else if (doubleClick) { - printf("double\n"); + //printf("double\n"); if (!ctrl) { mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); @@ -672,7 +680,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) } else if (click) { - printf("single\n"); + //printf("single\n"); mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); if (ctrl) mSelectionMode = SelectionMode::Word; @@ -767,10 +775,38 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) int appendIndex = 0; int longest = cTextStart; + // TODO contentSize test print + //if(alt) + //{ + // ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); + // auto contentSize = ImGui::GetWindowContentRegionMax(); + // auto breakAndLineNumberWidth = mCharAdvance.x * cTextStart; + // //std::cout << "( " << contentSize.x << ", " << contentSize.y << " )\n"; + // std::cout << "( " << cursorScreenPos.x << ", " << cursorScreenPos.y << " )\n"; + //} + + + + ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); auto scrollX = ImGui::GetScrollX(); auto scrollY = ImGui::GetScrollY(); + + /*const float breakpoinBarWidth = mCharAdvance.y; + auto breakBarA = ImVec2(cursorScreenPos.x, cursorScreenPos.y + scrollY); + auto breakBarB = ImVec2(breakBarA.x + breakpoinBarWidth, breakBarA.y + contentSize.y + scrollY); + drawList->AddRectFilled(breakBarA, breakBarB, ImGui::ColorConvertFloat4ToU32({ 0,0,1,0.5 })); + + auto breakAndLineNumberWidth = mCharAdvance.x * cTextStart; + auto lineBarA = ImVec2(cursorScreenPos.x + breakpoinBarWidth, cursorScreenPos.y + scrollY); + auto lineBarB = ImVec2(lineBarA.x + breakAndLineNumberWidth - breakpoinBarWidth, lineBarA.y + contentSize.y + scrollY); + drawList->AddRectFilled(lineBarA, lineBarB, ImGui::ColorConvertFloat4ToU32({ 1,0,0,0.5 })); + + auto textAreaA = ImVec2(cursorScreenPos.x + breakAndLineNumberWidth, cursorScreenPos.y + scrollY); + auto textAreaB = ImVec2(cursorScreenPos.x + contentSize.x + scrollX * 2, textAreaA.y + contentSize.y + scrollY); + drawList->AddRectFilled(textAreaA, textAreaB, ImGui::ColorConvertFloat4ToU32({ 0,1,0,0.5 }));*/ + auto lineNo = (int)floor(scrollY / mCharAdvance.y); auto lineMax = std::max(0, std::min((int)mLines.size() - 1, lineNo + (int)floor((scrollY + contentSize.y) / mCharAdvance.y))); if (!mLines.empty()) @@ -983,7 +1019,6 @@ void TextEditor::SetText(const std::string & aText) { mLines.push_back(Line()); mTextChanged = true; - LexAll(); } else { @@ -999,7 +1034,6 @@ void TextEditor::SetText(const std::string & aText) } mTextChanged = true; - LexAll(); } } @@ -1053,7 +1087,6 @@ void TextEditor::EnterCharacter(Char aChar) } mTextChanged = true; - LexAll(); u.mAdded = aChar; u.mAddedEnd = GetActualCursorCoordinates(); @@ -1434,7 +1467,6 @@ void TextEditor::Delete() } mTextChanged = true; - LexAll(); //Colorize(pos.mLine, 1); } @@ -1498,7 +1530,6 @@ void TextEditor::BackSpace() } mTextChanged = true; - LexAll(); EnsureCursorVisible(); //Colorize(mState.mCursorPosition.mLine, 1); diff --git a/TextEditor.h b/TextEditor.h index 15b2908c..52e2fb9a 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -12,9 +12,6 @@ #include #include "LuaToken.h" - -class LuaLexer; - class TextEditor { public: @@ -175,8 +172,6 @@ class TextEditor TextEditor(); ~TextEditor(); - void ClearLexer(); - /*void SetLanguageDefinition(const LanguageDefinition& aLanguageDef); const LanguageDefinition& GetLanguageDefinition() const { return mLanguageDefinition; }*/ @@ -317,6 +312,7 @@ class TextEditor std::string GetWordAt(const Coordinates& aCoords) const; bool MouseOverText() const; + bool MouseOverLineNumbers() const; bool MouseOverBreakpoints() const; ImVec2 MouseDistanceOutsideTextArea() const; @@ -355,7 +351,5 @@ class TextEditor ErrorMarkers mErrorMarkers; ImVec2 mCharAdvance; Coordinates mInteractiveStart, mInteractiveEnd; - - //LuaLexer* mLexer; }; From 508e0eaf1a0d3f9c7ef9e8a610446b7ee1abe1f8 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Mon, 30 Jul 2018 16:53:04 +0200 Subject: [PATCH 15/26] Replaced "string start end" to "string" in lexer tokens Now putting the name in name tokens Now putting message in malformed number token --- LuaLexer.cpp | 52 ++++++++++++------ LuaLexer.h | 5 +- LuaToken.h | 147 +++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 169 insertions(+), 35 deletions(-) diff --git a/LuaLexer.cpp b/LuaLexer.cpp index 3f5029bb..7af82377 100644 --- a/LuaLexer.cpp +++ b/LuaLexer.cpp @@ -1109,7 +1109,16 @@ void LuaLexer::ConsumeName(char c) const size_t identifierEnd = _col - 1; ColorRange(_identifierStart, identifierEnd, TextEditor::PaletteIndex::Identifier); - AddToken({ LuaToken::TYPE_NAME, { _identifierStart, identifierEnd} }); + + const size_t strSize = identifierEnd - _identifierStart + 1; + + std::string name; + name.reserve(strSize); + auto& glyphs = _lines[_line].mGlyphs; + for(size_t i = _identifierStart; i <= identifierEnd; ++i) + name.append(1, glyphs[i].mChar); + + AddToken({ LuaToken::TYPE_NAME, name }); } void LuaLexer::ConsumeNumber(char c) @@ -1137,20 +1146,18 @@ void LuaLexer::ConsumeNumber(char c) return ConsumeDotNumber(c); } - // TODO identifier characters here cause: malformed number near .n{n}e[-]{n}c - // TODO What other characters cause this??? Can end with == ~= or newline or similar if (IsBeginningOfIdentifier(c)) { - AddToken({ LuaToken::TYPE_ERROR_MALFORMED_NUMBER, _identifierStart, _col }); + // TODO all characters after the digits, until the next whitespace, should be in the message + MalformedNumber(_identifierStart, _col); return; } const size_t end = _col - 1; ColorRange(_identifierStart, end, TextEditor::PaletteIndex::Number); - AddToken({ LuaToken::TYPE_NUMBER, _identifierStart, end }); + AddToken({ LuaToken::TYPE_NUMBER }); } - void LuaLexer::ConsumeDotNumber(char c) { GetNext(); @@ -1178,10 +1185,11 @@ void LuaLexer::ConsumeDotNumber(char c) // Atleast one digit if (!isdigit(c)) { - AddToken({ LuaToken::TYPE_ERROR_MALFORMED_NUMBER, _identifierStart, _col }); + // TODO all characters after the 'e', until the next whitespace, should be in the message + MalformedNumber(_identifierStart, _col - 1); return; } - + GetNext(); // Consume all following digits @@ -1193,17 +1201,15 @@ void LuaLexer::ConsumeDotNumber(char c) } } - // TODO identifier characters here cause: malformed number near .n{n}e[-]{n}c - // TODO What other characters cause this??? Can end with == ~= or newline or similar if(IsBeginningOfIdentifier(c)) { - AddToken({ LuaToken::TYPE_ERROR_MALFORMED_NUMBER, _identifierStart, _col}); + MalformedNumber(_identifierStart, _col); return; } const size_t end = _col - 1; ColorRange(_identifierStart, end, TextEditor::PaletteIndex::Number); - AddToken({LuaToken::TYPE_NUMBER, _identifierStart, end}); + AddToken({ LuaToken::TYPE_NUMBER }); } void LuaLexer::ConsumeHexNumber(char c) @@ -1214,16 +1220,28 @@ void LuaLexer::ConsumeHexNumber(char c) c = PeekNext(); } - // TODO identifier characters here cause: malformed number near 0x{nx} - // TODO What other characters cause this??? Can end with == ~= or newline or similar if (IsBeginningOfIdentifier(c)) { - AddToken({ LuaToken::TYPE_ERROR_MALFORMED_NUMBER, _identifierStart, _col }); + MalformedNumber(_identifierStart, _col); return; } const size_t end = _col - 1; ColorRange(_identifierStart, end, TextEditor::PaletteIndex::Number); - AddToken({ LuaToken::TYPE_NUMBER, _identifierStart, end }); - return; + AddToken({ LuaToken::TYPE_NUMBER }); +} + +void LuaLexer::MalformedNumber(size_t start, size_t end) const +{ + auto& glyphs = _lines[_line].mGlyphs; + + std::string msg("malformed number near '"); + const size_t numSize = end - start + 2 + msg.size(); + msg.reserve(numSize + 1); + + for(size_t i = start; i <= end; ++i) + msg.append(1, glyphs[i].mChar); + msg.append(1, '\''); + + AddToken({ LuaToken::TYPE_ERROR_MALFORMED_NUMBER, msg }); } diff --git a/LuaLexer.h b/LuaLexer.h index 445f2ad8..8c5a5dc2 100644 --- a/LuaLexer.h +++ b/LuaLexer.h @@ -79,8 +79,7 @@ class LuaLexer const size_t stringEnd = _col - 1; - AddToken({ LuaToken::TYPE_STRING, stringStart, stringEnd }); - + AddToken({ LuaToken::TYPE_STRING}); return true; } @@ -116,6 +115,8 @@ class LuaLexer // c is first character peeked after x void ConsumeHexNumber(char c); + void MalformedNumber(size_t start, size_t end) const; + static bool IsBeginningOfIdentifier(char c) { return c >= 'a' && c <= 'z' || diff --git a/LuaToken.h b/LuaToken.h index c61b348d..d140cf00 100644 --- a/LuaToken.h +++ b/LuaToken.h @@ -64,23 +64,16 @@ struct LuaToken TYPE_STRING, TYPE_NUMBER, - TYPE_ERROR_STRAY_TILDE, + TYPE_EOS, + + TYPE_ERROR_STRAY_TILDE, // TODO Should probably handle this in another way. Original lexer doesn't do this + TYPE_ERROR_INVALID_LONG_STRING_DELIMITER, TYPE_ERROR_STRING, TYPE_ERROR_BRACKET, TYPE_ERROR_BAD_CHARACTER, TYPE_ERROR_MALFORMED_NUMBER, }; - struct StartEnd - { - StartEnd(size_t start, size_t end) - : _start(start), _end(end) - {} - - size_t _start; - size_t _end; - }; - struct Level { Level(size_t level) @@ -125,15 +118,137 @@ struct LuaToken : _type(type) {} - LuaToken(Type type, size_t start, size_t end) - : _type(type), _data(StartEnd(start, end)) + LuaToken(Type type, std::string str) + : _type(type), _data(std::move(str)) {} - LuaToken(Type type, Bracket bracket) + LuaToken(Type type, Bracket&& bracket) : _type(type), _data(bracket) {} Type _type; - std::variant _data; -}; \ No newline at end of file + std::variant _data; + + std::string ToString() const + { + switch (_type) + { + case TYPE_NAME: + return ""; + case TYPE_AND: + return "'and'"; + case TYPE_BREAK: + return "'break'"; + case TYPE_DO: + return "'do'"; + case TYPE_ELSE: + return "'else'"; + case TYPE_ELSEIF: + return "'elseif'"; + case TYPE_END: + return "'end'"; + case TYPE_FALSE: + return "'false'"; + case TYPE_FOR: + return "'for'"; + case TYPE_FUNCTION: + return "'function'"; + case TYPE_IF: + return "'if'"; + case TYPE_IN: + return "'in'"; + case TYPE_LOCAL: + return "'local'"; + case TYPE_NIL: + return "'nil'"; + case TYPE_NOT: + return "'not'"; + case TYPE_OR: + return "'or'"; + case TYPE_REPEAT: + return "'repeat'"; + case TYPE_RETURN: + return "'return'"; + case TYPE_THEN: + return "'then'"; + case TYPE_TRUE: + return "'true'"; + case TYPE_UNTIL: + return "'until'"; + case TYPE_WHILE: + return "'while'"; + case TYPE_PLUS: + return "'+'"; + case TYPE_MINUS: + return "'-'"; + case TYPE_MUL: + return "'*'"; + case TYPE_DIV: + return "'/'"; + case TYPE_MOD: + return "'%'"; + case TYPE_EXP: + return "'^'"; + case TYPE_HASH: + return "'#'"; + case TYPE_EQ: + return "'=='"; + case TYPE_NEQ: + return "'~='"; + case TYPE_LE: + return "'<='"; + case TYPE_GE: + return "'>='"; + case TYPE_LT: + return "'<'"; + case TYPE_GT: + return "'>'"; + case TYPE_ASSIGN: + return "'='"; + case TYPE_LEFT_B: + return "'('"; + case TYPE_RIGHT_B: + return "')'"; + case TYPE_LEFT_CB: + return "'{'"; + case TYPE_RIGHT_CB: + return "'}'"; + case TYPE_LEFT_SB: + return "'['"; + case TYPE_RIGHT_SB: + return "']'"; + case TYPE_SEMI_COLON: + return "';'"; + case TYPE_COLON: + return "':'"; + case TYPE_COMMA: + return "','"; + case TYPE_DOT: + return "'.'"; + case TYPE_CONCAT: + return "'..'"; + case TYPE_VARARG: + return "'...'"; + case TYPE_COMMENT: + return ""; + case TYPE_EOS: + return "'eos'"; + // TODO What to return on these? + case TYPE_BEGIN_LONG_COMMENT: + case TYPE_BEGIN_LONG_STRING: + case TYPE_END_LONG_BRACKET: + case TYPE_STRING: + case TYPE_NUMBER: + case TYPE_ERROR_STRAY_TILDE: + case TYPE_ERROR_INVALID_LONG_STRING_DELIMITER: + case TYPE_ERROR_STRING: + case TYPE_ERROR_BRACKET: + case TYPE_ERROR_BAD_CHARACTER: + case TYPE_ERROR_MALFORMED_NUMBER: + default: + return ""; + + } + } +}; From 91681d57328fa2f723145a4146c1048a858355ac Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Tue, 31 Jul 2018 15:46:16 +0200 Subject: [PATCH 16/26] Long comment and long strings are now lexed in a single go. Comments aren't added as a token anymore --- LuaLexer.cpp | 480 +++++++++++++++++++++++++-------------------------- LuaLexer.h | 45 ++--- LuaToken.h | 62 +------ 3 files changed, 256 insertions(+), 331 deletions(-) diff --git a/LuaLexer.cpp b/LuaLexer.cpp index 7af82377..fc01fabe 100644 --- a/LuaLexer.cpp +++ b/LuaLexer.cpp @@ -5,8 +5,6 @@ void LuaLexer::LexAll() { - if (_lines.empty()) - return; _line = 0; _col = 0; @@ -35,18 +33,10 @@ void LuaLexer::LexAll() ColorCurrent(TextEditor::PaletteIndex::Comment); GetNext(); ColorCurrent(TextEditor::PaletteIndex::Comment); - const size_t commentStart = _col - 2; - - auto [longComment, level] = ConsumeBeginLongComment(commentStart); - if(longComment) - { - if(!ConsumeLongBracket(level, TextEditor::PaletteIndex::Comment)) - return; - } - else + if(!ConsumeLongComment()) { - // Point at the second '-' or at the point ConsumeBeginLongComment have placed us + // Point at the second '-' or at the point ConsumeLongComment might have consumed some characters size_t commentEnd = _col - 1; // Consume characters (and increment commentEnd) until end of line or end of file @@ -54,10 +44,7 @@ void LuaLexer::LexAll() { c = PeekNext(); if (c == '\n' || c == '\0') - { - AddToken({ LuaToken::TYPE_COMMENT, commentStart , commentEnd }); break; - } ++commentEnd; @@ -159,16 +146,9 @@ void LuaLexer::LexAll() { const size_t stringStart = _col - 1; - auto[longString, level] = ConsumeBeginLongString(stringStart); - - if (longString) + if(!ConsumeLongString()) { - if (!ConsumeLongBracket(level, TextEditor::PaletteIndex::String)) - return; - } - else - { - // ConsumeBeginLongString might have failed to match. Need move _col back + // ConsumeLongString might have failed to match. Need move _col back _col = stringStart + 1; ColorCurrent(TextEditor::PaletteIndex::Default); @@ -225,13 +205,11 @@ void LuaLexer::LexAll() break; case '\'': ColorCurrent(TextEditor::PaletteIndex::String); - if(!ConsumeString<'\''>(c)) - return; + ConsumeString<'\''>(c); break; case '"': ColorCurrent(TextEditor::PaletteIndex::String); - if (!ConsumeString<'"'>(c)) - return; + ConsumeString<'"'>(c); break; default: @@ -257,6 +235,8 @@ void LuaLexer::LexAll() c = GetNext(); } + + AddToken({LuaToken::TYPE_EOS}); } char LuaLexer::GetNext() @@ -334,222 +314,6 @@ void LuaLexer::AddToken(LuaToken&& token) const _lines[_line].mTokens.emplace_back(token); } -std::tuple LuaLexer::ConsumeBeginLongComment(size_t pos) -{ - if (PeekNext() == '[') - { - GetNext(); - ColorCurrent(TextEditor::PaletteIndex::Comment); - - char c{ PeekNext() }; - if (c == '[') - { - GetNext(); - ColorCurrent(TextEditor::PaletteIndex::Comment); - AddToken({ LuaToken::TYPE_BEGIN_LONG_COMMENT, {0, pos} }); - return { true, 0 }; - } - - if (c == '=') - { - GetNext(); - ColorCurrent(TextEditor::PaletteIndex::Comment); - LuaToken::Level level {1}; - - bool search = true; - while (search) - { - c = PeekNext(); - - switch (c) - { - case '[': - GetNext(); - ColorCurrent(TextEditor::PaletteIndex::Comment); - AddToken({ LuaToken::TYPE_BEGIN_LONG_COMMENT, {level, pos} }); - return { false, level }; - case '=': - GetNext(); - ColorCurrent(TextEditor::PaletteIndex::Comment); - ++level; - break; - default: - search = false; - break; - } - } - } - } - - return { false, 0 }; -} - -// Returns true and level of bracket if a long bracket is detected, false and whatever otherwise -std::tuple LuaLexer::ConsumeBeginLongString(size_t pos) -{ - char c{ PeekNext() }; - if (c == '[') - { - ColorCurrent(TextEditor::PaletteIndex::String); - GetNext(); - ColorCurrent(TextEditor::PaletteIndex::String); - AddToken({ LuaToken::TYPE_BEGIN_LONG_STRING, {0, pos} }); - return { true, 0 }; - } - - if (c == '=') - { - ColorCurrent(TextEditor::PaletteIndex::String); - GetNext(); - LuaToken::Level level {1}; - - bool search = true; - while (search) - { - c = PeekNext(); - - switch (c) - { - case '[': - ColorCurrent(TextEditor::PaletteIndex::String); - GetNext(); - ColorCurrent(TextEditor::PaletteIndex::String); - AddToken({ LuaToken::TYPE_BEGIN_LONG_STRING, {level, pos} }); - return { true, level }; - case '=': - ColorCurrent(TextEditor::PaletteIndex::String); - GetNext(); - ++level; - break; - default: - // We have consumed '=' characters, but this wans't a long string. Caller need to move _col back - search = false; - break; - } - } - } - - return { false, 0 }; -} - -bool LuaLexer::ConsumeLongBracket(LuaToken::Level level, TextEditor::PaletteIndex color) -{ - if(level == 0) - { - char c{ PeekNext() }; - - bool search = true; - while (search) - { - switch (c) - { - case ']': - GetNext(); - ColorCurrent(color); - - c = PeekNext(); - switch (c) - { - case ']': - AddToken({ LuaToken::TYPE_END_LONG_BRACKET, LuaToken::Bracket(level, _col) }); - GetNext(); - ColorCurrent(color); - return true; - case '\0': - search = false; - break; - default: - GetNext(); - ColorCurrent(color); - c = PeekNext(); - break; - } - break; - case '\0': - search = false; - break; - default: - GetNext(); - ColorCurrent(color); - c = PeekNext(); - break; - } - } - } - else - { - char c{ PeekNext() }; - - bool searchBracket = true; - while (searchBracket) - { - LuaToken::Level currentLevel = 0; - - switch (c) - { - case ']': - { - GetNext(); - ColorCurrent(color); - c = PeekNext(); - - bool searchEquals = true; - while (searchEquals) - { - switch (c) - { - case ']': - if (level == currentLevel) - { - AddToken({ LuaToken::TYPE_END_LONG_BRACKET, LuaToken::Bracket(level, _col) }); - GetNext(); - ColorCurrent(color); - return true; - } - - GetNext(); - ColorCurrent(color); - searchEquals = false; - break; - case '=': - GetNext(); - ColorCurrent(color); - c = PeekNext(); - ++currentLevel; - if (currentLevel > level) - { - searchEquals = false; - } - break; - case '\0': - searchEquals = searchBracket = false; - break; - default: - GetNext(); - ColorCurrent(color); - c = PeekNext(); - searchEquals = false; - break; - } - } - } - break; - case '\0': - searchBracket = false; - break; - default: - GetNext(); - ColorCurrent(color); - c = PeekNext(); - break; - } - } - } - - AddToken({LuaToken::TYPE_ERROR_BRACKET}); - return false; -} - void LuaLexer::ConsumeIdentifier(char c) { _identifierStart = _col - 1; @@ -1245,3 +1009,231 @@ void LuaLexer::MalformedNumber(size_t start, size_t end) const AddToken({ LuaToken::TYPE_ERROR_MALFORMED_NUMBER, msg }); } + +bool LuaLexer::ConsumeLongComment() +{ + char c{ PeekNext() }; + + if (c != '[') + return false; + GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Comment); + + size_t targetLevel = 0; + + c = PeekNext(); + while (c == '=') + { + GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Comment); + ++targetLevel; + c = PeekNext(); + } + + if (c != '[') + return false; + GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Comment); + + c = PeekNext(); + + size_t firstLine = _line; + size_t commentStart = _col; + size_t curLineStartCol = _col; + + // Ignore first newline + if (c == '\n') + { + GetNext(); + c = PeekNext(); + ++firstLine; + commentStart = 0; + curLineStartCol = 0; + } + + bool firstBracketFound = false; + size_t level = 0; + size_t numChars = 0; + + bool search = true; + while (search) + { + switch (c) + { + case ']': + if (firstBracketFound) + { + if (level == targetLevel) + { + const size_t charsOnThisLine = _col - curLineStartCol - level - 1; + numChars += charsOnThisLine; + search = false; + } + else + level = 0; + } + else + firstBracketFound = true; + break; + case '=': + if (firstBracketFound) + ++level; + break; + case '\0': + AddToken(LuaToken::TYPE_ERROR_UNFINISHED_LONG_COMMENT); + return true; + case '\n': + numChars += _lines[_line].mGlyphs.size() - curLineStartCol + 1; + curLineStartCol = 0; + // Intentional fall through + default: + firstBracketFound = false; + level = 0; + break; + } + + GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Comment); + c = PeekNext(); + } + + std::string comment; + comment.reserve(numChars); + + const size_t numLines = _line - firstLine + 1; + if (numLines == 1) + { + const size_t lastChar = commentStart + numChars - 1; + auto& first = _lines[firstLine].mGlyphs; + for (auto i = commentStart; i <= lastChar; ++i) + comment.append(1, first[i].mChar); + } + else + { + auto& first = _lines[firstLine].mGlyphs; + for (auto i = commentStart; i < first.size(); ++i) + comment.append(1, first[i].mChar); + + for (size_t i = 1; i < numLines - 1; ++i) + { + comment.append(1, '\n'); + + auto& line = _lines[firstLine + i].mGlyphs; + for (auto& j : line) + comment.append(1, j.mChar); + } + + comment.append(1, '\n'); + + const size_t charsOnLastLine = _col - curLineStartCol - level - 2; + auto& line = _lines[firstLine + numLines - 1].mGlyphs; + for (size_t i = 0; i < charsOnLastLine; ++i) + comment.append(1, line[i].mChar); + } + + return true; +} + +bool LuaLexer::ConsumeLongString() +{ + ColorCurrent(TextEditor::PaletteIndex::String); + char c{ PeekNext() }; + + size_t targetLevel = 0; + + c = PeekNext(); + while (c == '=') + { + GetNext(); + ColorCurrent(TextEditor::PaletteIndex::String); + ++targetLevel; + c = PeekNext(); + } + + if (c != '[') + { + if (targetLevel == 0) + return false; + + std::string msg{ "invalid long string delimiter near '[" }; + msg.reserve(msg.size() + targetLevel + 1); + msg.append(targetLevel, '=').append("'"); + + AddToken({ LuaToken::TYPE_ERROR_INVALID_LONG_STRING_DELIMITER, msg }); + return true; + } + + GetNext(); + ColorCurrent(TextEditor::PaletteIndex::String); + + size_t line = _line; + + c = PeekNext(); + + // Ignore first newline + if (c == '\n') + { + GetNext(); + c = PeekNext(); + } + + bool firstBracketFound = false; + size_t level = 0; + + bool search = true; + while (search) + { + switch (c) + { + case ']': + if (firstBracketFound) + { + if (level == targetLevel) + { + search = false; + } + else + level = 0; + } + else + firstBracketFound = true; + break; + case '=': + if (firstBracketFound) + ++level; + break; + case '\0': + AddToken(LuaToken::TYPE_ERROR_UNFINISHED_LONG_STRING); + return true; + default: + firstBracketFound = false; + level = 0; + break; + } + + GetNext(); + ColorCurrent(TextEditor::PaletteIndex::String); + c = PeekNext(); + } + + // TODO Add long string to the first line + + _lines[line].mTokens.emplace_back(LuaToken::TYPE_STRING); + return true; +} + +void LuaLexer::UnfinishedString(size_t start) const +{ + auto& glyphs = _lines[_line].mGlyphs; + + const size_t end = glyphs.size(); + std::string msg("unfinished string near '"); + const size_t size = end - start + 1 + msg.size(); + msg.reserve(size); + + for (size_t i = start; i < end; ++i) + msg.append(1, glyphs[i].mChar); + msg.append(1, '\''); + + AddToken( { LuaToken::TYPE_ERROR_STRING, msg } ); +} diff --git a/LuaLexer.h b/LuaLexer.h index 8c5a5dc2..1d5fe094 100644 --- a/LuaLexer.h +++ b/LuaLexer.h @@ -28,69 +28,49 @@ class LuaLexer void AddToken(LuaToken&& token) const; template - bool ConsumeString(char c) + void ConsumeString(char c) { - const size_t stringStart = _col + 1; + const size_t stringStart = _col - 1; bool ignoreNext = false; bool searchString = true; while (searchString) { - c = GetNext(); + c = PeekNext(); ColorCurrent(TextEditor::PaletteIndex::String); switch (c) { case '\n': case '\0': - AddToken(LuaToken::TYPE_ERROR_STRING); - return false; + UnfinishedString(stringStart); + return; case '\\': + GetNext(); ignoreNext = !ignoreNext; break; case Delimiter: + GetNext(); + ColorCurrent(TextEditor::PaletteIndex::String); if (ignoreNext) - { ignoreNext = false; - } else - { searchString = false; - } break; default: + GetNext(); if (ignoreNext) { ignoreNext = false; // TODO Verify escape sequence } - else - { - // TODO Something? Probably not - } break; } - - if (c == '\n' || c == '\0') - { - searchString = false; - } } - const size_t stringEnd = _col - 1; - - AddToken({ LuaToken::TYPE_STRING}); - return true; + AddToken({ LuaToken::TYPE_STRING }); } - // Returns true and level of bracket if a long bracket is detected, false and whatever otherwise - std::tuple ConsumeBeginLongComment(size_t pos); - - // Returns true and level of bracket if a long bracket is detected, false and whatever otherwise - std::tuple ConsumeBeginLongString(size_t pos); - - bool ConsumeLongBracket(LuaToken::Level level, TextEditor::PaletteIndex color); - void ConsumeIdentifier(char c); void ConsumeAnd(); void ConsumeDo(); @@ -117,6 +97,11 @@ class LuaLexer void MalformedNumber(size_t start, size_t end) const; + bool ConsumeLongComment(); + bool ConsumeLongString(); + + void UnfinishedString(size_t start) const; + static bool IsBeginningOfIdentifier(char c) { return c >= 'a' && c <= 'z' || diff --git a/LuaToken.h b/LuaToken.h index d140cf00..86cb5c12 100644 --- a/LuaToken.h +++ b/LuaToken.h @@ -57,63 +57,20 @@ struct LuaToken TYPE_CONCAT, TYPE_VARARG, - TYPE_COMMENT, - TYPE_BEGIN_LONG_COMMENT, - TYPE_BEGIN_LONG_STRING, - TYPE_END_LONG_BRACKET, TYPE_STRING, TYPE_NUMBER, TYPE_EOS, TYPE_ERROR_STRAY_TILDE, // TODO Should probably handle this in another way. Original lexer doesn't do this + TYPE_ERROR_UNFINISHED_LONG_COMMENT, + TYPE_ERROR_UNFINISHED_LONG_STRING, TYPE_ERROR_INVALID_LONG_STRING_DELIMITER, TYPE_ERROR_STRING, - TYPE_ERROR_BRACKET, TYPE_ERROR_BAD_CHARACTER, TYPE_ERROR_MALFORMED_NUMBER, }; - struct Level - { - Level(size_t level) - : _level(level) - {} - - Level& operator++() - { - ++_level; - return *this; - } - - bool operator==(const Level& other) const - { - return _level == other._level; - } - - bool operator!=(const Level& other) const - { - return _level != other._level; - } - - bool operator>(const Level& other) const - { - return _level > other._level; - } - - size_t _level; - }; - - struct Bracket - { - Bracket(Level level, size_t pos) - : _level(level), _pos(pos) - {} - - Level _level; - size_t _pos; - }; - LuaToken(Type type) : _type(type) {} @@ -122,13 +79,9 @@ struct LuaToken : _type(type), _data(std::move(str)) {} - LuaToken(Type type, Bracket&& bracket) - : _type(type), _data(bracket) - {} - Type _type; - std::variant _data; + std::variant _data; std::string ToString() const { @@ -230,25 +183,20 @@ struct LuaToken return "'..'"; case TYPE_VARARG: return "'...'"; - case TYPE_COMMENT: - return ""; case TYPE_EOS: return "'eos'"; // TODO What to return on these? - case TYPE_BEGIN_LONG_COMMENT: - case TYPE_BEGIN_LONG_STRING: - case TYPE_END_LONG_BRACKET: case TYPE_STRING: case TYPE_NUMBER: case TYPE_ERROR_STRAY_TILDE: + case TYPE_ERROR_UNFINISHED_LONG_COMMENT: + case TYPE_ERROR_UNFINISHED_LONG_STRING: case TYPE_ERROR_INVALID_LONG_STRING_DELIMITER: case TYPE_ERROR_STRING: - case TYPE_ERROR_BRACKET: case TYPE_ERROR_BAD_CHARACTER: case TYPE_ERROR_MALFORMED_NUMBER: default: return ""; - } } }; From 43b07b8391b0450f525708d38481bf9d939c1530 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Sat, 4 Aug 2018 08:18:15 +0200 Subject: [PATCH 17/26] Fixed bug causing dot tokens to never be produced --- LuaLexer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/LuaLexer.cpp b/LuaLexer.cpp index fc01fabe..1c865628 100644 --- a/LuaLexer.cpp +++ b/LuaLexer.cpp @@ -173,8 +173,7 @@ void LuaLexer::LexAll() ColorCurrent(TextEditor::PaletteIndex::Punctuation); break; case '.': - c = PeekNext(); - if (c == '.') + if (PeekNext() == '.') { ColorCurrent(TextEditor::PaletteIndex::Punctuation); GetNext(); From 7fd88e7ab04e567c2f4a57d6d922e18be9ea6342 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Sat, 4 Aug 2018 08:25:13 +0200 Subject: [PATCH 18/26] Initial Parser implementation --- LuaBlock.h | 53 +++ LuaFunctionState.h | 170 ++++++++ LuaLexer.cpp | 7 +- LuaParser.cpp | 899 ++++++++++++++++++++++++++++++++++++++++++ LuaParser.h | 115 ++++++ LuaToken.h | 21 +- LuaVariable.h | 52 +++ LuaVariableLocation.h | 12 + TextEditor.cpp | 28 +- TextEditor.h | 6 +- 10 files changed, 1353 insertions(+), 10 deletions(-) create mode 100644 LuaBlock.h create mode 100644 LuaFunctionState.h create mode 100644 LuaParser.cpp create mode 100644 LuaParser.h create mode 100644 LuaVariable.h create mode 100644 LuaVariableLocation.h diff --git a/LuaBlock.h b/LuaBlock.h new file mode 100644 index 00000000..ba7693ad --- /dev/null +++ b/LuaBlock.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include "LuaVariable.h" +#include "LuaVariableLocation.h" + +class LuaBlock +{ + bool _breakable; + std::vector _locals; + +public: + + LuaBlock(bool breakable) + : _breakable(breakable) + {} + + bool IsBreakable() const + { + return _breakable; + } + + void AddLocal(std::string name, const LuaVariableLocation& loc, size_t count) + { + _locals.emplace_back(std::move(name), loc, loc._line, count); + } + + size_t GetLocalCount(const std::string& name) const + { + return std::count_if(_locals.begin(), _locals.end(), + [name](const LuaLocal& local) { return local._name == name; } + ); + } + + LuaVariable GetLocal(const std::string& name) const + { + const auto found = std::find_if(_locals.rbegin(), _locals.rend(), + [name](const LuaLocal& local) { return local._name == name; } + ); + + if(found != _locals.rend()) + return *found; + + return LuaVariable(); + } + + const std::vector& Close(size_t lastLineDefined) + { + for (auto& local : _locals) + local.SetLastLineDefined(lastLineDefined); + return _locals; + } +}; diff --git a/LuaFunctionState.h b/LuaFunctionState.h new file mode 100644 index 00000000..99e9c72d --- /dev/null +++ b/LuaFunctionState.h @@ -0,0 +1,170 @@ +#pragma once + +#include +#include "LuaBlock.h" +#include "LuaVariable.h" +#include +#include + +class LuaFunctionState +{ + size_t _lineDefined; + LuaFunctionState* _prevFunc = nullptr; + bool _isVararg; + + // All local declarations in the functions scope + std::vector _locals; + // All usages of upvalues for this function + std::vector _upvalues; + // Names of all used upvalues + std::vector _upvalueNames; + + // Active blocks + std::vector _blocks; + + // All references to a variable, in the function scope. + std::vector _variables; + +public: + LuaFunctionState(size_t lineDefined, LuaFunctionState* prevFunc = nullptr) + : _lineDefined(lineDefined), _prevFunc(prevFunc) + {} + + void SetVararg() + { + _isVararg = true; + } + + bool IsVararg() const + { + return _isVararg; + } + + bool IsBreakable() const + { + return std::any_of(_blocks.rbegin(), _blocks.rend(), + [](const LuaBlock& block){ return block.IsBreakable(); } + ); + } + + void OpenBlock(bool breakable) + { + _blocks.emplace_back(breakable); + } + + void CloseBlock(size_t lastLineDefined) + { + for (const auto& local : (*_blocks.rbegin()).Close(lastLineDefined)) + _variables.emplace_back(local); + _blocks.pop_back(); + } + + const std::vector& Close(size_t endLine) + { + // Close scope of all locals and add them to the variables + for (auto& local : _locals) + { + local.SetLastLineDefined(endLine); + _variables.emplace_back(local); + } + _locals.clear(); + + // Close scope of all upvalues and add them to the variables + for (auto& upvalue : _upvalues) + { + upvalue.SetLastLineDefined(endLine); + _variables.emplace_back(upvalue); + } + _upvalues.clear(); + + return _variables; + } + + void AddLocal(const std::string& name, const LuaVariableLocation& loc) + { + // TODO Verify std function + auto count = std::count_if(_locals.begin(), _locals.end(), + [name](LuaLocal& local) { return local._name == name; } + ); + + if(_blocks.empty()) + { + _locals.emplace_back(name, loc, loc._line, count); + } + else + { + // TODO Verify these std functions + count = std::accumulate(_blocks.begin(), _blocks.end(), count, + [name](size_t acc, const LuaBlock& block) { return acc + block.GetLocalCount(name); } + ); + + (*_blocks.rbegin()).AddLocal( name, loc, count); + } + } + + // Returns the closest value with this name. + // Turns locals outside the function into upvalues + LuaVariable Variable(const std::string& name, const LuaVariableLocation& loc, bool currentFunction = true) + { + // Check if local in a block + for(auto iter = _blocks.rbegin(); iter != _blocks.rend(); ++iter) + { + auto foundLocal = iter->GetLocal(name); + if(std::holds_alternative(foundLocal)) + { + auto local = std::get(foundLocal); + if (currentFunction) + _variables.emplace_back(LuaLocal(name, loc, local._lineDefined, local._count)); + + return local; + } + } + + // Check if local in function scope + const auto foundLocal = std::find_if(_locals.rbegin(), _locals.rend(), + [name](const LuaLocal& local) { return local._name == name; } + ); + if (foundLocal != _locals.rend()) + { + if (currentFunction) + _variables.emplace_back(LuaLocal(name, loc, foundLocal->_lineDefined, foundLocal->_count)); + return *foundLocal; + } + + // Check if upvalue + const auto foundUpvalue = std::find_if(_upvalueNames.rbegin(), _upvalueNames.rend(), + [name](const std::string& upvalueName) { return upvalueName == name; } + ); + if (foundUpvalue != _upvalueNames.rend()) + { + auto upvalue = LuaUpvalue(name, loc, _lineDefined); + if (currentFunction) + _variables.emplace_back(upvalue); + return upvalue; + } + + // Not a variable in this scope. Look at previous function + if(_prevFunc) + { + const auto res = _prevFunc->Variable(name, loc, false); + + // Upvalue or local in previous function? + if(std::holds_alternative(res) || std::holds_alternative(res)) + { + // Make into upvalue for this function + _upvalueNames.push_back(name); + + auto upvalue = LuaUpvalue(name, loc, _lineDefined); + if (currentFunction) + _upvalues.push_back(upvalue); + return upvalue; + } + } + + // Was a global + auto global = LuaGlobal(name, loc); + if(currentFunction) + _variables.emplace_back(global); + return global; + } +}; diff --git a/LuaLexer.cpp b/LuaLexer.cpp index 1c865628..f6bf22b8 100644 --- a/LuaLexer.cpp +++ b/LuaLexer.cpp @@ -51,6 +51,8 @@ void LuaLexer::LexAll() GetNext(); ColorCurrent(TextEditor::PaletteIndex::Comment); } while (true); + + // TODO Add comment as a token. Can be consumed separetely by the parser, to give documentation to functions } } else @@ -881,7 +883,7 @@ void LuaLexer::ConsumeName(char c) for(size_t i = _identifierStart; i <= identifierEnd; ++i) name.append(1, glyphs[i].mChar); - AddToken({ LuaToken::TYPE_NAME, name }); + AddToken({ LuaToken::TYPE_NAME, LuaToken::NameData{name, _identifierStart, identifierEnd } }); } void LuaLexer::ConsumeNumber(char c) @@ -1130,6 +1132,7 @@ bool LuaLexer::ConsumeLongComment() comment.append(1, line[i].mChar); } + // TODO Add comment as a token. Can be consumed separetely by the parser, to give documentation to functions return true; } @@ -1215,8 +1218,6 @@ bool LuaLexer::ConsumeLongString() c = PeekNext(); } - // TODO Add long string to the first line - _lines[line].mTokens.emplace_back(LuaToken::TYPE_STRING); return true; } diff --git a/LuaParser.cpp b/LuaParser.cpp new file mode 100644 index 00000000..f0721149 --- /dev/null +++ b/LuaParser.cpp @@ -0,0 +1,899 @@ +#include "LuaParser.h" + +bool LuaParser::ParseAll() +{ + // TODO Temporary, to not halt the program + //return true; + + if (_lines.empty()) + return true; + + _line = 0; + _col = 0; + _lines[0].mTokens.clear(); + + NextToken(); + if(_currentToken->_type == LuaToken::TYPE_EOS) + return true; + + try + { + OpenFunction(1); + _currentFunctionState->SetVararg(); /* main func. is always vararg */ + Chunk(); + + Check(LuaToken::TYPE_EOS); + + CloseFunction(_line); + } + catch(LuaSyntaxException& e) + { + return false; + } + + return true; +} + +std::pair LuaParser::GetError() +{ + return {_errorMsg, _errorLine}; +} + +const std::vector& LuaParser::GetVariables() const +{ + return _variables; +} + +void LuaParser::NextToken() +{ + std::vector& tokens = _lines[_line].mTokens; + + // After last token on this line? + if (_col == tokens.size()) + { + // More lines after this one? + if (_line + 1 < _lines.size()) + { + _col = 1; + ++_line; + + // Skip lines with no tokens + while(_lines[_line].mTokens.empty()) + { + ++_line; + } + + _currentToken = &_lines[_line].mTokens[0]; + return; + } + + // EOS + return; + } + + _currentToken = &tokens[_col++]; +} + +LuaToken* LuaParser::PeekToken() const +{ + std::vector& tokens = _lines[_line].mTokens; + + // After last token on this line? + if (_col == tokens.size()) + { + // More lines after this one? + if (_line + 1 < _lines.size()) + { + size_t line = _line + 1; + + // Skip lines with no tokens + while (_lines[line].mTokens.empty()) + { + ++line; + } + + return &_lines[line].mTokens[0]; + } + + // EOS + return &_lines[_line].mTokens[_col - 1]; + } + + return &tokens[_col]; +} + +bool LuaParser::BlockFollow() const +{ + switch (_currentToken->_type) + { + case LuaToken::TYPE_ELSE: case LuaToken::TYPE_ELSEIF: case LuaToken::TYPE_END: + case LuaToken::TYPE_UNTIL: case LuaToken::TYPE_EOS: + return true; + default: + return false; + } +} + +bool LuaParser::TestNext(LuaToken::Type type) +{ + if (_currentToken->_type == type) + { + NextToken(); + return true; + } + return false; +} + +void LuaParser::CheckMatch(LuaToken::Type what, LuaToken::Type who, size_t where) +{ + if (!TestNext(what)) + { + if (where == _line) + ErrorExpected(what); + else + { + SyntaxError(LuaToken::ToString(what) + " expected (to close " + + LuaToken::ToString(who) + " at line " + std::to_string(where) + ")"); + } + } +} + +void LuaParser::SyntaxError(const std::string& msg) +{ + // NOTE the error message i present in the editor is a stripped down + // version of the original. So i shouldn't try to copy the original syntax error function + + _errorMsg = msg + " near " + LuaToken::ToString(_currentToken->_type); + // TODO Error should maybe be presented on other line than current? + _errorLine = _line; + throw LuaSyntaxException(); +} + +void LuaParser::ErrorExpected(LuaToken::Type token) +{ + SyntaxError(LuaToken::ToString(token) + " expected"); +} + +void LuaParser::Check(LuaToken::Type type) +{ + if (_currentToken->_type != type) + { + ErrorExpected(type); + } +} + +void LuaParser::CheckNext(LuaToken::Type type) +{ + Check(type); + NextToken(); +} + +// +void LuaParser::Chunk() +{ + bool islast = false; + //enterlevel(ls); + while (!islast && !BlockFollow()) + { + islast = Statement(); + TestNext(LuaToken::TYPE_SEMI_COLON); + } + //leavelevel(ls); +} + +void LuaParser::Block() +{ + /* block -> chunk */ + _currentFunctionState->OpenBlock(false); + Chunk(); + // TODO Verify that line is ok + _currentFunctionState->CloseBlock(_line); +} + +void LuaParser::EnterBlock(bool breakable) +{ + _currentFunctionState->OpenBlock(breakable); +} + +void LuaParser::LeaveBlock() +{ + _currentFunctionState->CloseBlock(_line); +} + +void LuaParser::OpenFunction(size_t lineDefined) +{ + if(_functionStates.empty()) + { + _functionStates.emplace_back(lineDefined); + _currentFunctionState = &(*_functionStates.rbegin()); + } + else + { + auto previousFunctionState = &(*_functionStates.rbegin()); + _functionStates.emplace_back(lineDefined, previousFunctionState); + _currentFunctionState = &(*_functionStates.rbegin()); + } +} + +void LuaParser::CloseFunction(size_t lastLineDefined) +{ + if(_currentFunctionState == nullptr) + { + // ERROR + } + + // Add all variables in this function to the text editor + const auto& variables = _currentFunctionState->Close(lastLineDefined); + for (const auto& variable : variables) + _textEditor->AddVariable(variable); + + // Set previous function state to current + _functionStates.pop_back(); + if(_functionStates.empty()) + _currentFunctionState = nullptr; + else + _currentFunctionState = &(*_functionStates.rbegin()); +} + +bool LuaParser::FuncName() +{ + /* funcname -> NAME {field} [`:' NAME] */ + + bool needself = false; + Check(LuaToken::TYPE_NAME); + // TODO Function name is a varaible too? Might be local? + NextToken(); + while (_currentToken->_type == '.') + Field(); + + if (_currentToken->_type == ':') + { + needself = true; + Field(); + } + return needself; +} + +void LuaParser::FuncArgs() +{ + const auto line = _line; + switch (_currentToken->_type) + { + case LuaToken::TYPE_LEFT_B: + /* funcargs -> `(' [ explist1 ] `)' */ + // TODO Ambiguous syntax. Need to capture the line before this one + //if (line != ls->lastline) + // SyntaxError("ambiguous syntax (function call x new statement)"); + NextToken(); + if (_currentToken->_type != LuaToken::TYPE_RIGHT_B) // Arglist not empty? + { + ExpList1(); + } + CheckMatch(LuaToken::TYPE_RIGHT_B, LuaToken::TYPE_LEFT_B, line); + break; + + case LuaToken::TYPE_LEFT_CB: + /* funcargs -> constructor */ + Constructor(); + break; + case LuaToken::TYPE_STRING: + /* funcargs -> STRING */ + NextToken(); + break; + default: + SyntaxError("function arguments expected"); + break; + } +} + +void LuaParser::ExpList1() +{ + /* explist1 -> expr { `,' expr } */ + Expr(); + while (TestNext(LuaToken::TYPE_COMMA)) + { + Expr(); + } +} + +void LuaParser::Body(bool needSelf, size_t line) +{ + /* body -> `(' parlist `)' chunk END */ + + OpenFunction(line); + CheckNext(LuaToken::TYPE_LEFT_B); + if (needSelf) + { + // TODO SOMETHING????? Yeah. self is an implicit local variable. + // TODO Need to add it to function state, without treating it as a variable in the text + } + ParList(); + + CheckNext(LuaToken::TYPE_RIGHT_B); + Chunk(); + + const auto lastlinedefined = _line; + CheckMatch(LuaToken::TYPE_END, LuaToken::TYPE_FUNCTION, line); + CloseFunction(lastlinedefined); +} + + +void LuaParser::ParList() +{ + /* parlist -> [ param { `,' param } ] */ + if (_currentToken->_type != LuaToken::TYPE_RIGHT_B) + { /* is `parlist' not empty? */ + do + { + switch (_currentToken->_type) + { + case LuaToken::TYPE_NAME: + { /* param -> NAME */ + auto& data = std::get(_currentToken->_data); + _currentFunctionState->AddLocal(data._name, { _line, data._startCol, data._endCol }); + NextToken(); + break; + } + case LuaToken::TYPE_VARARG: + /* param -> `...' */ + NextToken(); + _currentFunctionState->SetVararg(); + break; + default: + SyntaxError(" or '...' expected"); + } + } while (!_currentFunctionState->IsVararg() && TestNext(LuaToken::TYPE_COMMA)); + } +} + +bool LuaParser::Statement() +{ + size_t line = _line; /* may be needed for error messages */ + switch (_currentToken->_type) + { + case LuaToken::TYPE_IF: + { /* stat -> ifstat */ + IfStat(line); + return false; + } + case LuaToken::TYPE_WHILE: + { /* stat -> whilestat */ + WhileStat(line); + return false; + } + case LuaToken::TYPE_DO: + { /* stat -> DO block END */ + NextToken(); /* skip DO */ + Block(); + CheckMatch(LuaToken::TYPE_END, LuaToken::TYPE_DO, line); + return false; + } + case LuaToken::TYPE_FOR: + { /* stat -> forstat */ + ForStat(line); + return false; + } + case LuaToken::TYPE_REPEAT: + { /* stat -> repeatstat */ + RepeatStat(line); + return false; + } + case LuaToken::TYPE_FUNCTION: + { + FuncStat(line); /* stat -> funcstat */ + return false; + } + case LuaToken::TYPE_LOCAL: + { /* stat -> localstat */ + NextToken(); /* skip LOCAL */ + if (TestNext(LuaToken::TYPE_FUNCTION)) /* local function? */ + LocalFunction(); + else + LocalStat(); + return false; + } + case LuaToken::TYPE_RETURN: + { /* stat -> retstat */ + RetStat(); + return true; /* must be last statement */ + } + case LuaToken::TYPE_BREAK: + { /* stat -> breakstat */ + NextToken(); /* skip BREAK */ + BreakStat(); + return true; /* must be last statement */ + } + default: + { + ExprStat(); + return false; /* to avoid warnings */ + } + } +} + +void LuaParser::IfStat(size_t line) +{ + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ + + TestThenBlock(); /* IF cond THEN block */ + while (_currentToken->_type == LuaToken::TYPE_ELSEIF) + { + TestThenBlock(); /* IF cond THEN block */ + } + if (_currentToken->_type == LuaToken::TYPE_ELSE) + { + NextToken(); // skip ELSE + Block(); /* `else' part */ + } + + CheckMatch(LuaToken::TYPE_END, LuaToken::TYPE_IF, line); +} + +void LuaParser::WhileStat(size_t line) +{ + /* whilestat -> WHILE cond DO block END */ + NextToken(); /* skip WHILE */ + Cond(); + EnterBlock(true); + CheckNext(LuaToken::TYPE_DO); + Block(); + CheckMatch(LuaToken::TYPE_END, LuaToken::TYPE_WHILE, line); + LeaveBlock(); +} + +void LuaParser::ForStat(size_t line) +{ + /* forstat -> FOR (fornum | forlist) END */ + + EnterBlock(true); /* scope for loop and control variables */ + NextToken(); /* skip `for' */ + + /* first variable name */ + Check(LuaToken::TYPE_NAME); + auto& data = std::get(_currentToken->_data); + _currentFunctionState->AddLocal(data._name, { _line, data._startCol, data._endCol }); + NextToken(); + + switch (_currentToken->_type) + { + case LuaToken::TYPE_ASSIGN: + ForNum(line); + break; + case LuaToken::TYPE_COMMA: + case LuaToken::TYPE_IN: + ForList(); + break; + default: + SyntaxError("'=' or 'in' expected"); + } + CheckMatch(LuaToken::TYPE_END, LuaToken::TYPE_FOR, line); + LeaveBlock(); /* loop scope (`break' jumps to this point) */ +} + +void LuaParser::RepeatStat(size_t line) +{ + /* repeatstat -> REPEAT block UNTIL cond */ + + EnterBlock(true); /* loop block */ + EnterBlock(false); /* scope block */ + NextToken(); /* skip REPEAT */ + Chunk(); + CheckMatch(LuaToken::TYPE_UNTIL, LuaToken::TYPE_REPEAT, line); + + Cond(); /* read condition (inside scope block) */ + + LeaveBlock(); /* finish scope */ + LeaveBlock(); /* finish loop */ +} + +void LuaParser::FuncStat(size_t line) +{ + /* funcstat -> FUNCTION funcname body */ + + // TODO Associate doc comment here + NextToken(); /* skip FUNCTION */ + const bool needself = FuncName(); + + Body(needself, line); +} + +void LuaParser::LocalFunction() +{ + // TODO Associating doc comment could be tricky here + // TODO The 'local' keyword could come in the way of the comment + Check(LuaToken::TYPE_NAME); + auto& data = std::get(_currentToken->_data); + _currentFunctionState->AddLocal(data._name, { _line, data._startCol, data._endCol }); + NextToken(); + + Body(false, _line); +} + +void LuaParser::LocalStat() +{ + std::vector> locals; + /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */ + do + { + Check(LuaToken::TYPE_NAME); + locals.emplace_back(&std::get(_currentToken->_data), _line); + NextToken(); + } while (TestNext(LuaToken::TYPE_COMMA)); + if (TestNext(LuaToken::TYPE_ASSIGN)) + ExpList1(); + + // Add locals after assignmnts are done + for (auto& local : locals) + { + _currentFunctionState->AddLocal(local.first->_name, + { local.second, local.first->_startCol, local.first->_endCol}); + } +} + +void LuaParser::RetStat() +{ + /* stat -> RETURN explist */ + + NextToken(); /* skip RETURN */ + if(!BlockFollow() && _currentToken->_type != LuaToken::TYPE_SEMI_COLON) + { + ExpList1(); /* optional return values */ + } +} + +void LuaParser::BreakStat() +{ + if(!_currentFunctionState->IsBreakable()) + SyntaxError("no loop to break"); +} + +void LuaParser::ExprStat() +{ + const bool functionCall = PrimaryExp(); + if (!functionCall) + { + Assignment(1); + } +} + +void LuaParser::TestThenBlock() +{ + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + NextToken(); /* skip IF or ELSEIF */ + Cond(); + CheckNext(LuaToken::TYPE_THEN); + Block(); /* `then' part */ +} + +void LuaParser::Cond() +{ + Expr(); +} + +void LuaParser::ForBody() +{ + /* forbody -> DO block */ + CheckNext(LuaToken::TYPE_DO); + EnterBlock(false); /* scope for declared variables */ + Block(); + LeaveBlock(); /* end of scope for declared variables */ +} + +void LuaParser::ForNum(size_t line) +{ + /* fornum -> NAME = exp1,exp1[,exp1] forbody */ + + // TODO NOTE Already made local + //new_localvar(ls, varname, 3); + CheckNext(LuaToken::TYPE_ASSIGN); + Expr(); /* initial value */ + CheckNext(LuaToken::TYPE_COMMA); + Expr(); /* limit */ + if (TestNext(LuaToken::TYPE_COMMA)) + Expr(); /* optional step */ + + ForBody(); +} + +void LuaParser::ForList() +{ + /* forlist -> NAME {,NAME} IN explist1 forbody */ + + while (TestNext(LuaToken::TYPE_COLON)) + { + Check(LuaToken::TYPE_NAME); + auto& data = std::get(_currentToken->_data); + _currentFunctionState->AddLocal(data._name, { _line, data._startCol, data._endCol }); + NextToken(); + } + + CheckNext(LuaToken::TYPE_IN); + ExpList1(); + ForBody(); +} + +void LuaParser::Assignment(int nVars) +{ + // TODO After the whole assingment the potential lh locals need to be added + + // TODO What does this condition check???? If it was a call? + //check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED, + // "syntax error"); + if (TestNext(LuaToken::TYPE_COMMA)) + { /* assignment -> `,' primaryexp assignment */ + + bool functionCall = PrimaryExp(); + // TODO Could check for illegal function call here + + //if (nv.v.k == VLOCAL) + // check_conflict(ls, lh, &nv.v); + //luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls, + // "variables in assignment"); + Assignment(nVars + 1); + } + else + { /* assignment -> `=' explist1 */ + CheckNext(LuaToken::TYPE_ASSIGN); + ExpList1(); + } +} + +bool LuaParser::IsUnopr() const +{ + switch (_currentToken->_type) + { + case LuaToken::TYPE_NOT: + case LuaToken::TYPE_MINUS: + case LuaToken::TYPE_HASH: + return true; + default: + return false; + } +} + +bool LuaParser::IsBinopr() const +{ + switch (_currentToken->_type) + { + case LuaToken::TYPE_PLUS: + case LuaToken::TYPE_MINUS: + case LuaToken::TYPE_MUL: + case LuaToken::TYPE_DIV: + case LuaToken::TYPE_MOD: + case LuaToken::TYPE_EXP: + case LuaToken::TYPE_CONCAT: + case LuaToken::TYPE_NEQ: + case LuaToken::TYPE_EQ: + case LuaToken::TYPE_LT: + case LuaToken::TYPE_LE: + case LuaToken::TYPE_GT: + case LuaToken::TYPE_GE: + case LuaToken::TYPE_AND: + case LuaToken::TYPE_OR: + return true; + default: + return false; + } +} + +void LuaParser::PrefixExp() +{ + /* prefixexp -> NAME | '(' expr ')' */ + switch (_currentToken->_type) + { + case LuaToken::TYPE_LEFT_B: + { + const auto line = _line; + NextToken(); + Expr(); + CheckMatch(LuaToken::TYPE_RIGHT_B, LuaToken::TYPE_LEFT_B, line); + } + break; + case LuaToken::TYPE_NAME: + { + auto& data = std::get(_currentToken->_data); + _currentFunctionState->Variable(data._name, {_line, data ._startCol, data._endCol}); + NextToken(); + } + break; + default: + SyntaxError("unexpected symbol"); + } +} + +bool LuaParser::PrimaryExp() +{ + /* primaryexp -> + prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */ + + bool functionCall = false; + + //FuncState *fs = ls->fs; + PrefixExp(); + for (;;) + { + switch (_currentToken->_type) + { + case LuaToken::TYPE_DOT: + { /* field */ + Field(); + functionCall = false; + break; + } + case LuaToken::TYPE_LEFT_SB: + { /* `[' exp1 `]' */ + YIndex(); + functionCall = false; + break; + } + case LuaToken::TYPE_COLON: + { /* `:' NAME funcargs */ + NextToken(); + Check(LuaToken::TYPE_NAME); + // TODO Add name to function state somehow? Would need relation to previous variables + NextToken(); + // TODO SELF?? + //luaK_self(fs, v, &key); + FuncArgs(); + functionCall = true; + break; + } + case LuaToken::TYPE_LEFT_B: + case LuaToken::TYPE_STRING: + case LuaToken::TYPE_LEFT_CB: + { /* funcargs */ + FuncArgs(); + functionCall = true; + break; + } + default: + return functionCall; + } + } +} + +void LuaParser::SimpleExp() +{ + /* simpleexp -> NUMBER | STRING | NIL | true | false | ... | + constructor | FUNCTION body | primaryexp */ + + switch (_currentToken->_type) + { + case LuaToken::TYPE_NUMBER: + //init_exp(v, VKNUM, 0); + //v->u.nval = ls->t.seminfo.r; + break; + case LuaToken::TYPE_STRING: + //codestring(ls, v, ls->t.seminfo.ts); + break; + case LuaToken::TYPE_NIL: + //init_exp(v, VNIL, 0); + break; + case LuaToken::TYPE_TRUE: + //init_exp(v, VTRUE, 0); + break; + case LuaToken::TYPE_FALSE: + //init_exp(v, VFALSE, 0); + break; + case LuaToken::TYPE_VARARG: + /* vararg */ + //FuncState *fs = ls->fs; + + // TODO Add error check + //check_condition(ls, fs->f->is_vararg, + // "cannot use " LUA_QL("...") " outside a vararg function"); + // TODO What does this line achieve? + //fs->f->is_vararg &= ~VARARG_NEEDSARG; /* don't need 'arg' */ + //init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); + break; + case LuaToken::TYPE_LEFT_CB: + /* constructor */ + Constructor(); + return; + case LuaToken::TYPE_FUNCTION: + NextToken(); + Body(false, _line); + return; + default: + PrimaryExp(); + return; + } + NextToken(); +} + +void LuaParser::Expr() +{ + SubExpr(); +} + +void LuaParser::SubExpr() +{ + // subexpr -> (simpleexp | unop subexpr) { binop subexpr } + //enterlevel(ls); + if (IsUnopr()) + { + NextToken(); + SubExpr(); + } + else + SimpleExp(); + + if (IsBinopr()) + { + NextToken(); + SubExpr(); + } + //leavelevel(ls); +} + +void LuaParser::YIndex() +{ + /* index -> '[' expr ']' */ + NextToken(); /* skip the '[' */ + Expr(); + CheckNext(LuaToken::TYPE_RIGHT_SB); +} + +void LuaParser::Field() +{ + /* field -> ['.' | ':'] NAME */ + NextToken(); + Check(LuaToken::TYPE_NAME); + // TODO add variable somehow + NextToken(); +} + +void LuaParser::Constructor() +{ + /* constructor -> ?? */ + const size_t line = _line; + + CheckNext(LuaToken::TYPE_LEFT_CB); + do + { + if (_currentToken->_type == LuaToken::TYPE_RIGHT_CB) + break; + + switch (_currentToken->_type) + { + case LuaToken::TYPE_NAME: + /* may be listfields or recfields */ + if (PeekToken()->_type != LuaToken::TYPE_ASSIGN) /* expression? */ + ListField(); + else + RecField(); + break; + case LuaToken::TYPE_LEFT_SB: + /* constructor_item -> recfield */ + RecField(); + break; + default: + /* constructor_part -> listfield */ + ListField(); + break; + } + } while (TestNext(LuaToken::TYPE_COMMA) || TestNext(LuaToken::TYPE_SEMI_COLON)); + CheckMatch(LuaToken::TYPE_RIGHT_CB, LuaToken::TYPE_LEFT_CB, line); +} + +void LuaParser::RecField() +{ + /* recfield -> (NAME | `['exp1`]') = exp1 */ + + if (_currentToken->_type == LuaToken::TYPE_NAME) + { + //luaY_checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); + //checkname(ls, &key); + NextToken(); + } + else /* ls->t.token == '[' */ + YIndex(); + CheckNext(LuaToken::TYPE_ASSIGN); + Expr(); +} + +void LuaParser::ListField() +{ + Expr(); + //luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); +} diff --git a/LuaParser.h b/LuaParser.h new file mode 100644 index 00000000..2d9f505d --- /dev/null +++ b/LuaParser.h @@ -0,0 +1,115 @@ +#pragma once +// This parser is influenced by the structure of lua5.1.5 lparser.c + +#include "TextEditor.h" +#include "LuaFunctionState.h" + +class LuaSymbolTable; + +class LuaParser +{ + class LuaSyntaxException : public std::exception + {}; + + // Used to add variable data to the text + TextEditor* _textEditor; + + TextEditor::Lines& _lines; + size_t _line; + size_t _col; + + LuaToken* _currentToken; + + std::list _functionStates; + LuaFunctionState* _currentFunctionState; + + std::vector _variables; + // TODO main chunk is always vararg + + std::string _errorMsg; + size_t _errorLine; + +public: + LuaParser(TextEditor::Lines& lines, TextEditor* textEditor) + : _textEditor(textEditor), _lines(lines), _line(0), _col(0), _currentToken(nullptr), + _currentFunctionState(nullptr), _errorLine(0) + {} + + bool ParseAll(); + + std::pair GetError(); + +private: + + const std::vector& GetVariables() const; + + void NextToken(); + LuaToken* PeekToken() const; + + bool BlockFollow() const; + + bool TestNext(LuaToken::Type type); // DONE + void CheckMatch(LuaToken::Type what, LuaToken::Type who, size_t where); + void SyntaxError(const std::string& msg); + void ErrorExpected(LuaToken::Type token); + + void Check(LuaToken::Type type); + void CheckNext(LuaToken::Type type); + + void Chunk(); // DONE + void Block(); + + void EnterBlock(bool breakable = false); + void LeaveBlock(); + + void OpenFunction(size_t lineDefined); + void CloseFunction(size_t lastLineDefined); + + // Return true if needs self + bool FuncName(); + void FuncArgs(); + + void ExpList1(); + void Body(bool needSelf, size_t line); + void ParList(); + + // Returns true if this is a last statement + bool Statement(); // DONE + void IfStat(size_t line); // DONE + void WhileStat(size_t line); + void ForStat(size_t line); + void RepeatStat(size_t line); + void FuncStat(size_t line); + void LocalFunction(); + void LocalStat(); + void RetStat(); + void BreakStat(); + void ExprStat(); + + void TestThenBlock(); // DONE + + void Cond(); + + void ForBody(); + void ForNum(size_t line); + void ForList(); + + void Assignment(int nVars); + + bool IsUnopr() const; // DONE + bool IsBinopr() const; // DONE + void PrefixExp(); // DONE + // Return true if it's a function call + bool PrimaryExp(); // DONE ISH + void SimpleExp(); // DONE ISH + void Expr(); //DONE + void SubExpr(); // DONE + + void YIndex(); // DONE + + void Field(); // DONE + + void Constructor(); + void RecField(); + void ListField(); +}; diff --git a/LuaToken.h b/LuaToken.h index 86cb5c12..1d035117 100644 --- a/LuaToken.h +++ b/LuaToken.h @@ -71,6 +71,17 @@ struct LuaToken TYPE_ERROR_MALFORMED_NUMBER, }; + struct NameData + { + NameData(std::string name, size_t startCol, size_t endCol) + : _name(std::move(name)), _startCol(startCol), _endCol(endCol) + {} + + std::string _name; + size_t _startCol; + size_t _endCol; + }; + LuaToken(Type type) : _type(type) {} @@ -79,13 +90,17 @@ struct LuaToken : _type(type), _data(std::move(str)) {} + LuaToken(Type type, NameData name) + : _type(type), _data(std::move(name)) + {} + Type _type; - std::variant _data; + std::variant _data; - std::string ToString() const + static std::string ToString(Type type) { - switch (_type) + switch (type) { case TYPE_NAME: return ""; diff --git a/LuaVariable.h b/LuaVariable.h new file mode 100644 index 00000000..df1c3140 --- /dev/null +++ b/LuaVariable.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include "LuaVariableLocation.h" + +struct LuaGlobal +{ + LuaGlobal(std::string name, const LuaVariableLocation& loc) + : _name(std::move(name)), _location(loc) + {} + + std::string _name; + LuaVariableLocation _location; +}; + +struct LuaLocal +{ + LuaLocal(std::string name, const LuaVariableLocation& loc, size_t lineDefined, size_t count) + : _name(std::move(name)), _location(loc), _count(count), _lineDefined(lineDefined), _lastLineDefined(0) + {} + + void SetLastLineDefined(size_t lastLineDefined) + { + _lastLineDefined = lastLineDefined; + } + + std::string _name; + LuaVariableLocation _location; + size_t _count; + size_t _lineDefined; + size_t _lastLineDefined; +}; + +struct LuaUpvalue +{ + LuaUpvalue(std::string name, const LuaVariableLocation& loc, size_t lineDefined) + : _name(std::move(name)), _location(loc), _lineDefined(lineDefined), _lastLineDefined(0) + {} + + void SetLastLineDefined(size_t lastLineDefined) + { + _lastLineDefined = lastLineDefined; + } + + std::string _name; + LuaVariableLocation _location; + size_t _lineDefined; + size_t _lastLineDefined; +}; + +typedef std::variant LuaVariable; diff --git a/LuaVariableLocation.h b/LuaVariableLocation.h new file mode 100644 index 00000000..52e85111 --- /dev/null +++ b/LuaVariableLocation.h @@ -0,0 +1,12 @@ +#pragma once + +struct LuaVariableLocation +{ + LuaVariableLocation(size_t line, size_t startCol, size_t endCol) + : _line(line), _startCol(startCol), _endCol(endCol) + {} + + size_t _line; + size_t _startCol; + size_t _endCol; +}; diff --git a/TextEditor.cpp b/TextEditor.cpp index 1f3045bd..f8cb8dba 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -7,6 +7,8 @@ #include "TextEditor.h" #include "LuaLexer.h" #include "imgui_internal.h" +#include "LuaParser.h" +#include static const int cTextStart = 7; @@ -50,8 +52,6 @@ TextEditor::TextEditor() { SetPalette(GetDarkPalette()); mLines.push_back(Line()); - - //mLexer = new LuaLexer(mLines); } @@ -533,12 +533,30 @@ void TextEditor::LexAll() LuaLexer(mLines).LexAll(); } +void TextEditor::ParseAll() +{ + // TODO Clear all variable inspection stuff + + LuaParser parser(mLines, this); + + if(!parser.ParseAll()) + { + // TODO Present error in better way + auto [msg, line] = parser.GetError(); + std::cout << "[" << line + 1 << "] " << msg << "\n"; + } +} + void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) { mWithinRender = true; if(mTextChanged) + { LexAll(); + ParseAll(); + } + mTextChanged = false; @@ -1772,6 +1790,12 @@ const TextEditor::Palette & TextEditor::GetRetroBluePalette() return p; } +void TextEditor::AddVariable(const LuaVariable& variable) +{ + // TODO Add the variables to the text, color the text based on variable type + + // TODO Add smarter variable inspection based on program counter +} std::string TextEditor::GetText() const { diff --git a/TextEditor.h b/TextEditor.h index 52e2fb9a..535161a9 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -11,6 +11,7 @@ #include "imgui.h" #include #include "LuaToken.h" +#include "LuaVariable.h" class TextEditor { @@ -237,6 +238,8 @@ class TextEditor static const Palette& GetLightPalette(); static const Palette& GetRetroBluePalette(); + void AddVariable(const LuaVariable& variable); + private: typedef std::vector> RegexList; @@ -317,8 +320,7 @@ class TextEditor ImVec2 MouseDistanceOutsideTextArea() const; void LexAll(); - - + void ParseAll(); float mLineSpacing; Lines mLines; From 845094b0092031610ac317e3beec89f6463ca267 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Sat, 4 Aug 2018 20:02:01 +0200 Subject: [PATCH 19/26] Fixed. Forlist tried to consume colon instead of comma. Function state didn't initialize isVararg, causing all functions to be vararg --- LuaFunctionState.h | 2 +- LuaParser.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LuaFunctionState.h b/LuaFunctionState.h index 99e9c72d..d7b3987d 100644 --- a/LuaFunctionState.h +++ b/LuaFunctionState.h @@ -27,7 +27,7 @@ class LuaFunctionState public: LuaFunctionState(size_t lineDefined, LuaFunctionState* prevFunc = nullptr) - : _lineDefined(lineDefined), _prevFunc(prevFunc) + : _lineDefined(lineDefined), _prevFunc(prevFunc), _isVararg(false) {} void SetVararg() diff --git a/LuaParser.cpp b/LuaParser.cpp index f0721149..e0b3879b 100644 --- a/LuaParser.cpp +++ b/LuaParser.cpp @@ -599,7 +599,7 @@ void LuaParser::ForList() { /* forlist -> NAME {,NAME} IN explist1 forbody */ - while (TestNext(LuaToken::TYPE_COLON)) + while (TestNext(LuaToken::TYPE_COMMA)) { Check(LuaToken::TYPE_NAME); auto& data = std::get(_currentToken->_data); From cbc373020da168cc0511ffb6a4d8d6979624f888 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Sun, 5 Aug 2018 20:09:27 +0200 Subject: [PATCH 20/26] Fix. Non local functions are now added as a variable too --- LuaParser.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/LuaParser.cpp b/LuaParser.cpp index e0b3879b..ec97b0ec 100644 --- a/LuaParser.cpp +++ b/LuaParser.cpp @@ -241,7 +241,8 @@ bool LuaParser::FuncName() bool needself = false; Check(LuaToken::TYPE_NAME); - // TODO Function name is a varaible too? Might be local? + auto& data = std::get(_currentToken->_data); + _currentFunctionState->Variable(data._name, { _line, data._startCol, data._endCol }); NextToken(); while (_currentToken->_type == '.') Field(); From 78f7d0ba8941afa79c12f8903c0b2a4a448ac295 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Sun, 5 Aug 2018 20:10:26 +0200 Subject: [PATCH 21/26] Ignoring *.TMP files in repository --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 259148fa..07fb5923 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ *.exe *.out *.app +*.TMP From 201e1130da41446e45b0010cb6464a3bf643b741 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Sun, 5 Aug 2018 20:42:51 +0200 Subject: [PATCH 22/26] Fixed. For loop control variables are now propperly scoped --- LuaParser.cpp | 16 +++++++++------- LuaParser.h | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/LuaParser.cpp b/LuaParser.cpp index ec97b0ec..0326ae67 100644 --- a/LuaParser.cpp +++ b/LuaParser.cpp @@ -195,9 +195,9 @@ void LuaParser::EnterBlock(bool breakable) _currentFunctionState->OpenBlock(breakable); } -void LuaParser::LeaveBlock() +void LuaParser::LeaveBlock(size_t lastLineDefined) { - _currentFunctionState->CloseBlock(_line); + _currentFunctionState->CloseBlock(lastLineDefined); } void LuaParser::OpenFunction(size_t lineDefined) @@ -439,7 +439,7 @@ void LuaParser::WhileStat(size_t line) CheckNext(LuaToken::TYPE_DO); Block(); CheckMatch(LuaToken::TYPE_END, LuaToken::TYPE_WHILE, line); - LeaveBlock(); + LeaveBlock(_line); } void LuaParser::ForStat(size_t line) @@ -467,8 +467,10 @@ void LuaParser::ForStat(size_t line) default: SyntaxError("'=' or 'in' expected"); } + // Intercept last line here before consuming end + const size_t lastLineDefined = _line; CheckMatch(LuaToken::TYPE_END, LuaToken::TYPE_FOR, line); - LeaveBlock(); /* loop scope (`break' jumps to this point) */ + LeaveBlock(lastLineDefined); /* loop scope (`break' jumps to this point) */ } void LuaParser::RepeatStat(size_t line) @@ -483,8 +485,8 @@ void LuaParser::RepeatStat(size_t line) Cond(); /* read condition (inside scope block) */ - LeaveBlock(); /* finish scope */ - LeaveBlock(); /* finish loop */ + LeaveBlock(_line); /* finish scope */ + LeaveBlock(_line); /* finish loop */ } void LuaParser::FuncStat(size_t line) @@ -577,7 +579,7 @@ void LuaParser::ForBody() CheckNext(LuaToken::TYPE_DO); EnterBlock(false); /* scope for declared variables */ Block(); - LeaveBlock(); /* end of scope for declared variables */ + LeaveBlock(_line); /* end of scope for declared variables */ } void LuaParser::ForNum(size_t line) diff --git a/LuaParser.h b/LuaParser.h index 2d9f505d..1f0ac3d5 100644 --- a/LuaParser.h +++ b/LuaParser.h @@ -60,7 +60,7 @@ class LuaParser void Block(); void EnterBlock(bool breakable = false); - void LeaveBlock(); + void LeaveBlock(size_t lastLineDefined); void OpenFunction(size_t lineDefined); void CloseFunction(size_t lastLineDefined); From 2d977053856b90657396238e03b40d00c4a2c762 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Mon, 6 Aug 2018 14:16:22 +0200 Subject: [PATCH 23/26] LuaBlock and LuaFunctionState now properly close all locals scope --- LuaBlock.h | 19 ++++++++++++++----- LuaFunctionState.h | 17 +++++++++++------ LuaVariable.h | 2 +- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/LuaBlock.h b/LuaBlock.h index ba7693ad..9f216696 100644 --- a/LuaBlock.h +++ b/LuaBlock.h @@ -7,6 +7,9 @@ class LuaBlock { bool _breakable; + // All local declarations in the blocks scope + std::vector _localDefinitions; + // All locals in the blocks scope std::vector _locals; public: @@ -20,25 +23,31 @@ class LuaBlock return _breakable; } - void AddLocal(std::string name, const LuaVariableLocation& loc, size_t count) + void AddLocalDefinition(const std::string& name, const LuaVariableLocation& loc, size_t count) { - _locals.emplace_back(std::move(name), loc, loc._line, count); + _localDefinitions.emplace_back(name, loc, loc._line, count); + _locals.emplace_back(name, loc, loc._line, count); + } + + void AddLocal(LuaLocal&& local) + { + _locals.emplace_back(local); } size_t GetLocalCount(const std::string& name) const { - return std::count_if(_locals.begin(), _locals.end(), + return std::count_if(_localDefinitions.begin(), _localDefinitions.end(), [name](const LuaLocal& local) { return local._name == name; } ); } LuaVariable GetLocal(const std::string& name) const { - const auto found = std::find_if(_locals.rbegin(), _locals.rend(), + const auto found = std::find_if(_localDefinitions.rbegin(), _localDefinitions.rend(), [name](const LuaLocal& local) { return local._name == name; } ); - if(found != _locals.rend()) + if(found != _localDefinitions.rend()) return *found; return LuaVariable(); diff --git a/LuaFunctionState.h b/LuaFunctionState.h index d7b3987d..e2c54d3b 100644 --- a/LuaFunctionState.h +++ b/LuaFunctionState.h @@ -13,6 +13,8 @@ class LuaFunctionState bool _isVararg; // All local declarations in the functions scope + std::vector _localDefinitions; + // All locals in the functions scope std::vector _locals; // All usages of upvalues for this function std::vector _upvalues; @@ -83,12 +85,13 @@ class LuaFunctionState void AddLocal(const std::string& name, const LuaVariableLocation& loc) { // TODO Verify std function - auto count = std::count_if(_locals.begin(), _locals.end(), + auto count = std::count_if(_localDefinitions.begin(), _localDefinitions.end(), [name](LuaLocal& local) { return local._name == name; } ); if(_blocks.empty()) { + _localDefinitions.emplace_back(name, loc, loc._line, count); _locals.emplace_back(name, loc, loc._line, count); } else @@ -98,7 +101,7 @@ class LuaFunctionState [name](size_t acc, const LuaBlock& block) { return acc + block.GetLocalCount(name); } ); - (*_blocks.rbegin()).AddLocal( name, loc, count); + (*_blocks.rbegin()).AddLocalDefinition(name, loc, count); } } @@ -114,20 +117,22 @@ class LuaFunctionState { auto local = std::get(foundLocal); if (currentFunction) - _variables.emplace_back(LuaLocal(name, loc, local._lineDefined, local._count)); + { + _blocks.rbegin()->AddLocal(LuaLocal(name, loc, local._lineDefined, local._count)); + } return local; } } // Check if local in function scope - const auto foundLocal = std::find_if(_locals.rbegin(), _locals.rend(), + const auto foundLocal = std::find_if(_localDefinitions.rbegin(), _localDefinitions.rend(), [name](const LuaLocal& local) { return local._name == name; } ); - if (foundLocal != _locals.rend()) + if (foundLocal != _localDefinitions.rend()) { if (currentFunction) - _variables.emplace_back(LuaLocal(name, loc, foundLocal->_lineDefined, foundLocal->_count)); + _locals.emplace_back(LuaLocal(name, loc, foundLocal->_lineDefined, foundLocal->_count)); return *foundLocal; } diff --git a/LuaVariable.h b/LuaVariable.h index df1c3140..69f1376b 100644 --- a/LuaVariable.h +++ b/LuaVariable.h @@ -27,7 +27,7 @@ struct LuaLocal std::string _name; LuaVariableLocation _location; - size_t _count; + size_t _count; // The number of locals with the same name preceding this one size_t _lineDefined; size_t _lastLineDefined; }; From 8a096a68c3171a87616f6f1de26ca0ec4fd23072 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Mon, 6 Aug 2018 14:18:25 +0200 Subject: [PATCH 24/26] Initial coloring and inspection of lua variables in the editor --- TextEditor.cpp | 87 ++++++++++++++++++++++++++++++++++++++++++++++++-- TextEditor.h | 14 ++++++++ 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index f8cb8dba..4d7e24bd 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -535,7 +535,7 @@ void TextEditor::LexAll() void TextEditor::ParseAll() { - // TODO Clear all variable inspection stuff + mVariables.clear(); LuaParser parser(mLines, this); @@ -972,6 +972,45 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) ++lineNo; } + const auto wordPos = FindWordStart(ScreenPosToCoordinates(ImGui::GetMousePos())); + //std::cout << wordPos.mLine << ", " << wordPos.mColumn << "\n"; + const auto found = std::find_if(mVariables.begin(), mVariables.end(), + [wordPos](const std::pair& var) { return var.first.mLine == wordPos.mLine && var.first.mColumn == wordPos.mColumn; } + ); + if (found != mVariables.end()) + { + std::string text; + bool valid = false; + + if (std::holds_alternative(found->second)) + { + auto& global = std::get(found->second); + text = "global: " + global._name; + valid = true; + } + else if (std::holds_alternative(found->second)) + { + auto& local = std::get(found->second); + text = "local: " + local._name + " [" + std::to_string(local._lineDefined + 1) + ", " + + std::to_string(local._lastLineDefined + 1) + "][" + std::to_string(local._count) + "]\n"; + valid = true; // TODO For now + } + else + { + auto& upvalue = std::get(found->second); + text = "upvalue: " + upvalue._name + " [" + std::to_string(upvalue._lineDefined + 1) + ", " + + std::to_string(upvalue._lastLineDefined + 1) + "]\n"; + valid = true; // TODO For now + } + + if(valid) + { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(text.c_str()); + ImGui::EndTooltip(); + } + } + auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos())); if (!id.empty()) { @@ -1719,6 +1758,11 @@ const TextEditor::Palette & TextEditor::GetDarkPalette() 0xffaaaaaa, // Identifier 0xff9bc64d, // Known identifier 0xffc040a0, // Preproc identifier + + 0xff0000ff, // GlobalValue, + 0xff00ff00, // LocalValue, + 0xffff0000, // UpValue, + 0xff206020, // Comment (single line) 0xff406020, // Comment (multi line) 0xff101010, // Background @@ -1747,6 +1791,11 @@ const TextEditor::Palette & TextEditor::GetLightPalette() 0xff404040, // Identifier 0xff606010, // Known identifier 0xffc040a0, // Preproc identifier + + 0xff0000ff, // GlobalValue, + 0xff00ff00, // LocalValue, + 0xffff0000, // UpValue, + 0xff205020, // Comment (single line) 0xff405020, // Comment (multi line) 0xffffffff, // Background @@ -1775,6 +1824,11 @@ const TextEditor::Palette & TextEditor::GetRetroBluePalette() 0xff00ffff, // Identifier 0xffffffff, // Known identifier 0xffff00ff, // Preproc identifier + + 0xff0000ff, // GlobalValue, + 0xff00ff00, // LocalValue, + 0xffff0000, // UpValue, + 0xff808080, // Comment (single line) 0xff404040, // Comment (multi line) 0xff800000, // Background @@ -1792,9 +1846,36 @@ const TextEditor::Palette & TextEditor::GetRetroBluePalette() void TextEditor::AddVariable(const LuaVariable& variable) { - // TODO Add the variables to the text, color the text based on variable type + LuaVariableLocation loc{0,0,0}; + PaletteIndex paletteIndex; + + if(std::holds_alternative(variable)) + { + auto& global = std::get(variable); + loc = global._location; + paletteIndex = PaletteIndex::GlobalValue; + } + else if (std::holds_alternative(variable)) + { + auto& local = std::get(variable); + loc = local._location; + paletteIndex = PaletteIndex::LocalValue; + } + else + { + auto& upvalue = std::get(variable); + loc = upvalue._location; + paletteIndex = PaletteIndex::UpValue; + } + + auto& glyphs = mLines[loc._line].mGlyphs; + for (size_t i = loc._startCol; i <= loc._endCol; ++i) + { + glyphs[i].mColorIndex = paletteIndex; + } - // TODO Add smarter variable inspection based on program counter + auto pair = std::make_pair(Coordinates(loc._line, loc._startCol), variable); + mVariables.insert(pair); } std::string TextEditor::GetText() const diff --git a/TextEditor.h b/TextEditor.h index 535161a9..aa736e5b 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -28,6 +28,9 @@ class TextEditor Identifier, KnownIdentifier, PreprocIdentifier, + GlobalValue, + LocalValue, + UpValue, Comment, MultiLineComment, Background, @@ -113,6 +116,14 @@ class TextEditor return mLine > o.mLine; return mColumn >= o.mColumn; } + + struct Hash { + std::size_t operator ()(Coordinates const& p) const + { + using std::hash; + return hash()(p.mLine) ^ hash()(p.mColumn); + } + }; }; struct Identifier @@ -124,6 +135,7 @@ class TextEditor typedef std::string String; typedef std::unordered_map Identifiers; typedef std::unordered_set Keywords; + typedef std::unordered_map Variables; typedef std::map ErrorMarkers; typedef std::unordered_set Breakpoints; typedef std::array Palette; @@ -353,5 +365,7 @@ class TextEditor ErrorMarkers mErrorMarkers; ImVec2 mCharAdvance; Coordinates mInteractiveStart, mInteractiveEnd; + + Variables mVariables; }; From ee3a8d223cd512d8289a91a82c3638826ac41a60 Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Mon, 6 Aug 2018 21:34:52 +0200 Subject: [PATCH 25/26] Added callbacks for requesting the value of globals, locals and upvalues. Shows the values when variables are hovered now. Now using a constant instead of magical value of -1 to indicate when program counter is disabled --- LuaVariable.h | 10 +++++++++ TextEditor.cpp | 56 +++++++++++++++++++++++++++++++++++++++----------- TextEditor.h | 8 ++++++++ 3 files changed, 62 insertions(+), 12 deletions(-) diff --git a/LuaVariable.h b/LuaVariable.h index 69f1376b..2bbd58c0 100644 --- a/LuaVariable.h +++ b/LuaVariable.h @@ -25,6 +25,11 @@ struct LuaLocal _lastLineDefined = lastLineDefined; } + bool InScope(size_t line) const + { + return line > _lineDefined + 1 && line <= _lastLineDefined + 1; + } + std::string _name; LuaVariableLocation _location; size_t _count; // The number of locals with the same name preceding this one @@ -43,6 +48,11 @@ struct LuaUpvalue _lastLineDefined = lastLineDefined; } + bool InScope(size_t line) const + { + return line > _lineDefined + 1 && line <= _lastLineDefined + 1; + } + std::string _name; LuaVariableLocation _location; size_t _lineDefined; diff --git a/TextEditor.cpp b/TextEditor.cpp index 4d7e24bd..44ce15a4 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -46,9 +46,12 @@ TextEditor::TextEditor() , mSelectionMode(SelectionMode::Normal) , mTextAreaHeld(false) //, mCheckMultilineComments(true) - , mCurrentStatement(-1) + , mCurrentStatement(0) , mBreakpointsModified(false) , mBreakpointsModifiedCallback(nullptr) + , mGetGlobalValueCallback(nullptr) + , mGetLocalValueCallback(nullptr) + , mGetUpValueCallback(nullptr) { SetPalette(GetDarkPalette()); mLines.push_back(Line()); @@ -78,7 +81,7 @@ void TextEditor::SetStatementMarker(int line) { mCurrentStatement = line; - if(mCurrentStatement != -1) + if(mCurrentStatement != NoStatement) { EnsureLineVisible(line); } @@ -870,7 +873,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) } // Draw statement marker - if(mCurrentStatement - 1 == lineNo) + if(mCurrentStatement != NoStatement && mCurrentStatement - 1 == lineNo) { const float breakpointRadius = mCharAdvance.y / 2 - 2; const float triangleRadius = breakpointRadius - 1; @@ -984,23 +987,46 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) if (std::holds_alternative(found->second)) { - auto& global = std::get(found->second); - text = "global: " + global._name; - valid = true; + if(mGetGlobalValueCallback != nullptr) + { + auto& global = std::get(found->second); + text = mGetGlobalValueCallback(global._name); + valid = true; + } } else if (std::holds_alternative(found->second)) { + if (mGetLocalValueCallback != nullptr) + { + auto& local = std::get(found->second); + if (local.InScope(static_cast(mCurrentStatement))) + { + text = mGetLocalValueCallback(local._name, local._count); + valid = !text.empty(); + } + } + + // Debugging auto& local = std::get(found->second); - text = "local: " + local._name + " [" + std::to_string(local._lineDefined + 1) + ", " + - std::to_string(local._lastLineDefined + 1) + "][" + std::to_string(local._count) + "]\n"; - valid = true; // TODO For now + text.append(" [" + std::to_string(local._lineDefined + 1) + ", " + + std::to_string(local._lastLineDefined + 1) + "]"); } else { + if (mGetUpValueCallback != nullptr) + { + auto& upvalue = std::get(found->second); + if (upvalue.InScope(static_cast(mCurrentStatement))) + { + text = mGetUpValueCallback(upvalue._name); + valid = !text.empty(); + } + } + + // Debugging auto& upvalue = std::get(found->second); - text = "upvalue: " + upvalue._name + " [" + std::to_string(upvalue._lineDefined + 1) + ", " + - std::to_string(upvalue._lastLineDefined + 1) + "]\n"; - valid = true; // TODO For now + text.append(" [" + std::to_string(upvalue._lineDefined + 1) + ", " + + std::to_string(upvalue._lastLineDefined + 1) + "]"); } if(valid) @@ -1009,6 +1035,12 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) ImGui::TextUnformatted(text.c_str()); ImGui::EndTooltip(); } + else + { + ImGui::BeginTooltip(); + ImGui::TextUnformatted("Not available"); + ImGui::EndTooltip(); + } } auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos())); diff --git a/TextEditor.h b/TextEditor.h index aa736e5b..371d09eb 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -140,6 +140,8 @@ class TextEditor typedef std::unordered_set Breakpoints; typedef std::array Palette; typedef char Char; + + static const int NoStatement {0}; struct Glyph { @@ -196,6 +198,9 @@ class TextEditor void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers ; mBreakpointsModified = true; } const Breakpoints& GetBreakpoints() const { return mBreakpoints; } void SetBreakPointsChangedCallback(std::function callback) { mBreakpointsModifiedCallback = std::move(callback); } + void SetGetGlobalValueCallback(std::function callback) { mGetGlobalValueCallback = std::move(callback); } + void SetGetLocalValueCallback(std::function callback) { mGetLocalValueCallback = std::move(callback); } + void SetGetUpValueCallback(std::function callback) { mGetUpValueCallback = std::move(callback); } void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); void SetText(const std::string& aText); @@ -362,6 +367,9 @@ class TextEditor Breakpoints mBreakpoints; bool mBreakpointsModified; std::function mBreakpointsModifiedCallback; + std::function mGetGlobalValueCallback; + std::function mGetLocalValueCallback; + std::function mGetUpValueCallback; ErrorMarkers mErrorMarkers; ImVec2 mCharAdvance; Coordinates mInteractiveStart, mInteractiveEnd; From a350599587000183b831eca310ebad64efbb08fb Mon Sep 17 00:00:00 2001 From: Mattias Ramqvist Date: Mon, 6 Aug 2018 22:53:45 +0200 Subject: [PATCH 26/26] Update README.md --- README.md | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 4d2ea58b..35fdf67b 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,11 @@ # ImGuiColorTextEdit -Syntax highlighting text editor for ImGui -![Screenshot](https://github.com/BalazsJako/ImGuiColorTextEdit/blob/master/ImGuiTextEdit.png "Screenshot") +A specialization of ImGuiColorTextEdit focusing solely on Lua 5.1. -Demo project: https://github.com/BalazsJako/ColorTextEditorDemo +Main Features: + - A bar for breakpoints to the left of the line numbers. + - Lexer for syntax highlighting and proper multiline comment and string support. + - Parser that can detect syntax errors and output data for globals, upvalues and locals (scope included). -This is my attempt to write a relatively simple widget which provides source code editing functionality with basic syntax highlighting. - -While it relies on Omar Cornut's https://github.com/ocornut/imgui, it does not follow the "pure" one widget - one function approach. Since the editor has to maintain a relatively complex internal state, it did not seem to be practical to try and enforce fully immediate mode. - -The code is work in progress, please report if you find any issues. - -Main features are: - - approximates typical code editor look and feel (essential mouse/keyboard commands work - I mean, the commands _I_ normally use :)) - - undo/redo support - - extensible, multiple language syntax support - - identifier declarations: a small piece of text associated with an identifier. The editor displays it in a tooltip when the mouse cursor is hovered over the identifier - - error markers: the user can specify a list of error messages together the line of occurence, the editor will highligh the lines with red backround and display error message in a tooltip when the mouse cursor is hovered over the line - - supports large files: there is no explicit limit set on file size or number of lines, performance is not affected when large files are loaded (except syntax coloring, see below) - - color palette support: you can switch between different color palettes, or even define your own - -Known issues: - - syntax highligthing is based on std::regex, which is diasppointingly slow. Because of that, the highlighting process is amortized between multiple frames. Hand-written colorizers and/or a lexical scanner might help resolve this problem. - - 8 bit character only, no Unicode or Utf support (yet) - - no variable-width font support - - there's no find/replace support - -Don't forget to post your screenshots if you use this little piece of software in order to keep me motivated. :) +Drawbacks: + - Does a full lexing and parsing pass every time a change is made to the text. But it's still pretty fast even for large files.