diff --git a/.gitignore b/.gitignore index 259148fa..07fb5923 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ *.exe *.out *.app +*.TMP diff --git a/LuaBlock.h b/LuaBlock.h new file mode 100644 index 00000000..9f216696 --- /dev/null +++ b/LuaBlock.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include "LuaVariable.h" +#include "LuaVariableLocation.h" + +class LuaBlock +{ + bool _breakable; + // All local declarations in the blocks scope + std::vector _localDefinitions; + // All locals in the blocks scope + std::vector _locals; + +public: + + LuaBlock(bool breakable) + : _breakable(breakable) + {} + + bool IsBreakable() const + { + return _breakable; + } + + void AddLocalDefinition(const std::string& name, const LuaVariableLocation& loc, size_t 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(_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(_localDefinitions.rbegin(), _localDefinitions.rend(), + [name](const LuaLocal& local) { return local._name == name; } + ); + + if(found != _localDefinitions.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..e2c54d3b --- /dev/null +++ b/LuaFunctionState.h @@ -0,0 +1,175 @@ +#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 _localDefinitions; + // All locals 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), _isVararg(false) + {} + + 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(_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 + { + // 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()).AddLocalDefinition(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) + { + _blocks.rbegin()->AddLocal(LuaLocal(name, loc, local._lineDefined, local._count)); + } + + return local; + } + } + + // Check if local in function scope + const auto foundLocal = std::find_if(_localDefinitions.rbegin(), _localDefinitions.rend(), + [name](const LuaLocal& local) { return local._name == name; } + ); + if (foundLocal != _localDefinitions.rend()) + { + if (currentFunction) + _locals.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 new file mode 100644 index 00000000..f6bf22b8 --- /dev/null +++ b/LuaLexer.cpp @@ -0,0 +1,1239 @@ +#include "LuaLexer.h" + +#include "LuaToken.h" +#include + +void LuaLexer::LexAll() +{ + + _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); + ColorCurrent(TextEditor::PaletteIndex::Default); + break; + case '-': + + if (PeekNext() == '-') // Comment or long comment + { + ColorCurrent(TextEditor::PaletteIndex::Comment); + GetNext(); + ColorCurrent(TextEditor::PaletteIndex::Comment); + + if(!ConsumeLongComment()) + { + // 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 + do + { + c = PeekNext(); + if (c == '\n' || c == '\0') + break; + + ++commentEnd; + + 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 + { + 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 + AddToken(LuaToken::TYPE_ASSIGN); + break; + 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 + 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 '[': + { + const size_t stringStart = _col - 1; + + if(!ConsumeLongString()) + { + // ConsumeLongString 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() == '.') + { + ColorCurrent(TextEditor::PaletteIndex::Punctuation); + GetNext(); + if (PeekNext() == '.') + { + ColorCurrent(TextEditor::PaletteIndex::Punctuation); + GetNext(); + AddToken(LuaToken::TYPE_VARARG); + } + else + 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); + ConsumeString<'\''>(c); + break; + case '"': + ColorCurrent(TextEditor::PaletteIndex::String); + ConsumeString<'"'>(c); + break; + default: + + // Start for ConsumeNumber + _identifierStart = _col - 1; + + if (IsBeginningOfIdentifier(c)) + { + ConsumeIdentifier(c); + break; + } + + if(isdigit(c)) + { + ConsumeNumber(c); + break; + } + + ColorCurrent(TextEditor::PaletteIndex::ErrorMarker); + AddToken({ LuaToken::TYPE_ERROR_BAD_CHARACTER }); + break; + } + + c = GetNext(); + } + + AddToken({LuaToken::TYPE_EOS}); +} + +char LuaLexer::GetNext() +{ + //std::cout << "GetNext: \t" << _line << ", " << _col << " ; "; + 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(); + + //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; +} + +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()) + { + //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()); + _lines[_line].mTokens.emplace_back(token); +} + +void LuaLexer::ConsumeIdentifier(char c) +{ + _identifierStart = _col - 1; + + switch (c) + { + case 'a': + return ConsumeAnd(); + case 'b': + return ConsumeBreak(); + case 'd': + return ConsumeDo(); + case 'e': + return ConsumeEndElseElseif(); + case 'f': + return ConsumeFalseForFunction(); + case 'i': + return ConsumeIfIn(); + case 'l': + return ConsumeLocal(); + case 'n': + return ConsumeNilNot(); + case 'o': + return ConsumeOr(); + case 'r': + return ConsumeRepeatReturn(); + case 't': + return ConsumeThenTrue(); + case 'u': + return ConsumeUntil(); + case 'w': + return ConsumeWhile(); + default: + return ConsumeName(PeekNext()); + } +} + +void LuaLexer::ConsumeAnd() +{ + char c = PeekNext(); + if(c != 'n') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'd') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if(IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_AND}); +} + +void LuaLexer::ConsumeDo() +{ + char c = PeekNext(); + if (c != 'o') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_DO}); +} + +void LuaLexer::ConsumeBreak() +{ + char c = PeekNext(); + if (c != 'r') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'a') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'k') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_BREAK}); +} + +void LuaLexer::ConsumeFalseForFunction() +{ + char c = PeekNext(); + switch(c) + { + case 'a': + GetNext(); + + c = PeekNext(); + if (c != 'l') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 's') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_FALSE}); + break; + case 'o': + GetNext(); + + c = PeekNext(); + if (c != 'r') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_FOR}); + break; + case 'u': + GetNext(); + + c = PeekNext(); + if (c != 'n') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'c') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 't') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'i') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'o') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'n') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_FUNCTION}); + break; + default: + return ConsumeName(c); + } +} + +void LuaLexer::ConsumeIfIn() +{ + char c = PeekNext(); + switch (c) + { + case 'f': + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_IF}); + break; + case 'n': + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_IN}); + break; + default: + return ConsumeName(c); + } +} + +void LuaLexer::ConsumeLocal() +{ + char c = PeekNext(); + if (c != 'o') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'c') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'a') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'l') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_LOCAL}); +} + +void LuaLexer::ConsumeEndElseElseif() +{ + char c = PeekNext(); + switch (c) + { + case 'n': + GetNext(); + + c = PeekNext(); + if (c != 'd') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_END}); + break; + case 'l': + GetNext(); + return ConsumeElseElseif(); + default: + return ConsumeName(c); + } +} + +void LuaLexer::ConsumeElseElseif() +{ + char c = PeekNext(); + if (c != 's') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'e') + 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(c); + GetNext(); + + c = PeekNext(); + if (c != 'f') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_ELSEIF}); +} + +void LuaLexer::ConsumeNilNot() +{ + char c = PeekNext(); + switch (c) + { + case 'i': + GetNext(); + + c = PeekNext(); + if (c != 'l') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_NIL}); + break; + case 'o': + GetNext(); + + c = PeekNext(); + if (c != 't') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_NOT}); + break; + default: + return ConsumeName(c); + } +} + +void LuaLexer::ConsumeOr() +{ + char c = PeekNext(); + if (c != 'r') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_OR}); +} + +void LuaLexer::ConsumeRepeatReturn() +{ + char c = PeekNext(); + if (c != 'e') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + switch (c) + { + case 'p': + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'a') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 't') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_REPEAT}); + break; + case 't': + GetNext(); + + c = PeekNext(); + if (c != 'u') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'r') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'n') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_RETURN}); + break; + default: + return ConsumeName(c); + } +} + +void LuaLexer::ConsumeThenTrue() +{ + char c = PeekNext(); + + switch (c) + { + case 'h': + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'n') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_THEN}); + break; + case 'r': + GetNext(); + + c = PeekNext(); + if (c != 'u') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_TRUE}); + break; + default: + return ConsumeName(c); + } +} + +void LuaLexer::ConsumeUntil() +{ + char c = PeekNext(); + if (c != 'n') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 't') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'i') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'l') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_UNTIL}); +} + +void LuaLexer::ConsumeWhile() +{ + char c = PeekNext(); + if (c != 'h') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'i') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'l') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (c != 'e') + return ConsumeName(c); + GetNext(); + + c = PeekNext(); + if (IsPartOfIdentifier(c)) + return ConsumeName(c); + + ColorRange(_identifierStart, _col - 1, TextEditor::PaletteIndex::Keyword); + AddToken({LuaToken::TYPE_WHILE}); +} + +void LuaLexer::ConsumeName(char c) +{ + while(IsPartOfIdentifier(c)) + { + GetNext(); + c = PeekNext(); + } + + const size_t identifierEnd = _col - 1; + + ColorRange(_identifierStart, identifierEnd, TextEditor::PaletteIndex::Identifier); + + 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, LuaToken::NameData{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); + } + + if (IsBeginningOfIdentifier(c)) + { + // 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 }); +} + +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)) + { + // 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 + c = PeekNext(); + while (isdigit(c)) + { + GetNext(); + c = PeekNext(); + } + } + + if(IsBeginningOfIdentifier(c)) + { + MalformedNumber(_identifierStart, _col); + return; + } + + const size_t end = _col - 1; + ColorRange(_identifierStart, end, TextEditor::PaletteIndex::Number); + AddToken({ LuaToken::TYPE_NUMBER }); +} + +void LuaLexer::ConsumeHexNumber(char c) +{ + while(isxdigit(c)) + { + GetNext(); + c = PeekNext(); + } + + if (IsBeginningOfIdentifier(c)) + { + MalformedNumber(_identifierStart, _col); + return; + } + + const size_t end = _col - 1; + ColorRange(_identifierStart, end, TextEditor::PaletteIndex::Number); + 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 }); +} + +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); + } + + // TODO Add comment as a token. Can be consumed separetely by the parser, to give documentation to functions + 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(); + } + + _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 new file mode 100644 index 00000000..1d5fe094 --- /dev/null +++ b/LuaLexer.h @@ -0,0 +1,121 @@ +#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 ColorCurrent(TextEditor::PaletteIndex color) const; + void ColorRange(size_t begin, size_t end, TextEditor::PaletteIndex color) const; + + void AddToken(LuaToken&& token) const; + + template + void ConsumeString(char c) + { + const size_t stringStart = _col - 1; + bool ignoreNext = false; + + bool searchString = true; + while (searchString) + { + c = PeekNext(); + ColorCurrent(TextEditor::PaletteIndex::String); + + switch (c) + { + case '\n': + case '\0': + 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 + } + break; + } + } + + AddToken({ LuaToken::TYPE_STRING }); + } + + 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(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); + + 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' || + 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/LuaParser.cpp b/LuaParser.cpp new file mode 100644 index 00000000..0326ae67 --- /dev/null +++ b/LuaParser.cpp @@ -0,0 +1,902 @@ +#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(size_t lastLineDefined) +{ + _currentFunctionState->CloseBlock(lastLineDefined); +} + +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); + auto& data = std::get(_currentToken->_data); + _currentFunctionState->Variable(data._name, { _line, data._startCol, data._endCol }); + 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(_line); +} + +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"); + } + // Intercept last line here before consuming end + const size_t lastLineDefined = _line; + CheckMatch(LuaToken::TYPE_END, LuaToken::TYPE_FOR, line); + LeaveBlock(lastLineDefined); /* 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(_line); /* finish scope */ + LeaveBlock(_line); /* 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(_line); /* 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_COMMA)) + { + 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..1f0ac3d5 --- /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(size_t lastLineDefined); + + 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 new file mode 100644 index 00000000..1d035117 --- /dev/null +++ b/LuaToken.h @@ -0,0 +1,217 @@ +#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_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_BAD_CHARACTER, + 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) + {} + + LuaToken(Type type, std::string str) + : _type(type), _data(std::move(str)) + {} + + LuaToken(Type type, NameData name) + : _type(type), _data(std::move(name)) + {} + + Type _type; + + std::variant _data; + + static std::string ToString(Type type) + { + 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_EOS: + return "'eos'"; + // TODO What to return on these? + 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_BAD_CHARACTER: + case TYPE_ERROR_MALFORMED_NUMBER: + default: + return ""; + } + } +}; diff --git a/LuaVariable.h b/LuaVariable.h new file mode 100644 index 00000000..2bbd58c0 --- /dev/null +++ b/LuaVariable.h @@ -0,0 +1,62 @@ +#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; + } + + 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 + 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; + } + + bool InScope(size_t line) const + { + return line > _lineDefined + 1 && line <= _lastLineDefined + 1; + } + + 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/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. diff --git a/TextEditor.cpp b/TextEditor.cpp index 14d3cff7..44ce15a4 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -5,6 +5,10 @@ #include #include "TextEditor.h" +#include "LuaLexer.h" +#include "imgui_internal.h" +#include "LuaParser.h" +#include static const int cTextStart = 7; @@ -29,41 +33,60 @@ bool equals(InputIt1 first1, InputIt1 last1, TextEditor::TextEditor() : mLineSpacing(0.0f) , mUndoIndex(0) + , mUndoSaveIndex(0) , mTabSize(4) , mOverwrite(false) , mReadOnly(false) , mWithinRender(false) , mScrollToCursor(false) + , mShouldScrollToLine(false) , mTextChanged(false) - , mColorRangeMin(0) - , mColorRangeMax(0) + //, mColorRangeMin(0) + //, mColorRangeMax(0) , mSelectionMode(SelectionMode::Normal) - , mCheckMultilineComments(true) + , mTextAreaHeld(false) + //, mCheckMultilineComments(true) + , mCurrentStatement(0) + , mBreakpointsModified(false) + , mBreakpointsModifiedCallback(nullptr) + , mGetGlobalValueCallback(nullptr) + , mGetLocalValueCallback(nullptr) + , mGetUpValueCallback(nullptr) { SetPalette(GetDarkPalette()); - SetLanguageDefinition(LanguageDefinition::HLSL()); mLines.push_back(Line()); } TextEditor::~TextEditor() { -} - -void TextEditor::SetLanguageDefinition(const LanguageDefinition & aLanguageDef) -{ - mLanguageDefinition = aLanguageDef; - mRegexList.clear(); - for (auto& r : mLanguageDefinition.mTokenRegexStrings) - mRegexList.push_back(std::make_pair(std::regex(r.first, std::regex_constants::optimize), r.second)); } +//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; } +void TextEditor::SetStatementMarker(int line) +{ + mCurrentStatement = line; + + if(mCurrentStatement != NoStatement) + { + EnsureLineVisible(line); + } +} + int TextEditor::AppendBuffer(std::string& aBuffer, char chr, int aIndex) { if (chr != '\t') @@ -95,8 +118,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; @@ -115,11 +138,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); @@ -131,7 +154,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 { @@ -152,15 +175,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); @@ -192,16 +215,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].mGlyphs.size()) + { + auto& newLine = InsertLine(aWhere.mLine + 1); + auto& line = mLines[aWhere.mLine]; + 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 + { + InsertLine(aWhere.mLine + 1); + } } else { - InsertLine(aWhere.mLine + 1); + InsertLine(aWhere.mLine); } ++aWhere.mLine; aWhere.mColumn = 0; @@ -210,7 +240,7 @@ 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); @@ -225,6 +255,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; @@ -243,9 +279,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; @@ -255,6 +291,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; @@ -263,13 +306,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; } @@ -284,13 +327,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; } @@ -303,10 +346,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) @@ -398,14 +441,126 @@ 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; } +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); +} + +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(); + 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(); + 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::LexAll() +{ + LuaLexer(mLines).LexAll(); +} + +void TextEditor::ParseAll() +{ + mVariables.clear(); + + 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; ImGuiIO& io = ImGui::GetIO(); @@ -422,10 +577,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; @@ -502,67 +663,132 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) 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(); + mTextAreaHeld = click || ImGui::IsMouseDown(0); } - else if (click) + else if(MouseOverBreakpoints()) // Check for breakpoint changes { - printf("single\n"); - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); - if (ctrl) - mSelectionMode = SelectionMode::Word; - else - mSelectionMode = SelectionMode::Normal; - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + 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); + } - lastClick = ImGui::GetTime(); + mBreakpointsModified = true; + } + } } - else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) + } + + //if (!ImGui::IsMouseDown(0)) + // mWordSelectionMode = false; + } + + // Continue to select text while mouse is held + if (mTextAreaHeld) + { + const float scrollCounterMax = 4.0f; + static float scrollVerticalCounter = scrollCounterMax; + + // Not holding the button down anymore? + if(!ImGui::IsMouseDown(0)) + { + mTextAreaHeld = 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(); + //ColorizeInternal(); static std::string buffer; auto contentSize = ImGui::GetWindowContentRegionMax(); @@ -570,10 +796,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()) @@ -584,10 +838,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; @@ -613,8 +867,28 @@ 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); + } + + // Draw statement marker + if(mCurrentStatement != NoStatement && 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); @@ -672,9 +946,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; @@ -701,10 +975,79 @@ 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)) + { + 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.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.append(" [" + std::to_string(upvalue._lineDefined + 1) + ", " + + std::to_string(upvalue._lastLineDefined + 1) + "]"); + } + + if(valid) + { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(text.c_str()); + ImGui::EndTooltip(); + } + else + { + ImGui::BeginTooltip(); + ImGui::TextUnformatted("Not available"); + ImGui::EndTooltip(); + } + } + 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(); @@ -720,7 +1063,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) ImGui::TextUnformatted(pi->second.mDeclaration.c_str()); ImGui::EndTooltip(); } - } + }*/ } } @@ -734,6 +1077,22 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) mScrollToCursor = false; } + if(mShouldScrollToLine) + { + EnsureLineVisible(mScrollToLine); + ImGui::SetWindowFocus(); + mShouldScrollToLine = false; + } + + if(mBreakpointsModified) + { + if(mBreakpointsModifiedCallback) + { + mBreakpointsModifiedCallback(this); + } + mBreakpointsModified = false; + } + ImGui::PopAllowKeyboardFocus(); ImGui::EndChild(); ImGui::PopStyleVar(); @@ -745,23 +1104,33 @@ 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()); + mTextChanged = true; + } + 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().mGlyphs.emplace_back(chr, PaletteIndex::Default); + } - mTextChanged = true; + mTextChanged = true; + } } + mUndoIndex = 0; + mUndoSaveIndex = 0; mUndoBuffer.clear(); - Colorize(); + //Colorize(); } void TextEditor::EnterCharacter(Char aChar) @@ -791,17 +1160,17 @@ 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; } @@ -814,7 +1183,7 @@ void TextEditor::EnterCharacter(Char aChar) AddUndo(u); - Colorize(coord.mLine - 1, 3); + //Colorize(coord.mLine - 1, 3); EnsureCursorVisible(); } @@ -867,7 +1236,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; @@ -895,7 +1264,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() @@ -909,7 +1278,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) @@ -981,7 +1350,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 @@ -1022,7 +1391,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) { @@ -1032,7 +1401,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); } @@ -1118,7 +1487,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) { @@ -1164,7 +1533,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; @@ -1174,21 +1543,21 @@ 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; - Colorize(pos.mLine, 1); + //Colorize(pos.mLine, 1); } u.mAfter = mState; @@ -1230,8 +1599,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; @@ -1240,19 +1609,19 @@ 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; EnsureCursorVisible(); - Colorize(mState.mCursorPosition.mLine, 1); + //Colorize(mState.mCursorPosition.mLine, 1); } u.mAfter = mState; @@ -1287,7 +1656,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()); } @@ -1368,6 +1737,46 @@ void TextEditor::Redo(int aSteps) mUndoBuffer[mUndoIndex++].Redo(this); } +void TextEditor::MarkSaved() +{ + mUndoSaveIndex = mUndoIndex; +} + +void TextEditor::MarkDirty() +{ + mUndoSaveIndex = -1; +} + +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 = { { @@ -1381,6 +1790,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 @@ -1409,6 +1823,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 @@ -1437,6 +1856,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 @@ -1452,6 +1876,39 @@ const TextEditor::Palette & TextEditor::GetRetroBluePalette() return p; } +void TextEditor::AddVariable(const LuaVariable& variable) +{ + 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; + } + + auto pair = std::make_pair(Coordinates(loc._line, loc._startCol), variable); + mVariables.insert(pair); +} std::string TextEditor::GetText() const { @@ -1467,178 +1924,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; } @@ -1708,14 +2165,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; @@ -1728,399 +2185,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 9613b513..371d09eb 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -9,6 +9,9 @@ #include #include #include "imgui.h" +#include +#include "LuaToken.h" +#include "LuaVariable.h" class TextEditor { @@ -25,6 +28,9 @@ class TextEditor Identifier, KnownIdentifier, PreprocIdentifier, + GlobalValue, + LocalValue, + UpValue, Comment, MultiLineComment, Background, @@ -110,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 @@ -121,10 +135,13 @@ 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; typedef char Char; + + static const int NoStatement {0}; struct Glyph { @@ -135,10 +152,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; @@ -160,19 +182,25 @@ 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 SetLanguageDefinition(const LanguageDefinition& aLanguageDef); + const LanguageDefinition& GetLanguageDefinition() const { return mLanguageDefinition; }*/ 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; } + 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); @@ -217,11 +245,18 @@ class TextEditor bool CanRedo() const; void Undo(int aSteps = 1); void Redo(int aSteps = 1); + void MarkSaved(); + void MarkDirty(); + bool IsDirty() const; + + void EnsureLineVisible(int line); static const Palette& GetDarkPalette(); static const Palette& GetLightPalette(); static const Palette& GetRetroBluePalette(); + void AddVariable(const LuaVariable& variable); + private: typedef std::vector> RegexList; @@ -268,9 +303,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; @@ -283,6 +318,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; @@ -295,29 +331,49 @@ class TextEditor std::string GetWordUnderCursor() const; std::string GetWordAt(const Coordinates& aCoords) const; + bool MouseOverText() const; + bool MouseOverLineNumbers() const; + bool MouseOverBreakpoints() const; + ImVec2 MouseDistanceOutsideTextArea() const; + + void LexAll(); + void ParseAll(); + float mLineSpacing; Lines mLines; EditorState mState; UndoBuffer mUndoBuffer; int mUndoIndex; - + int mUndoSaveIndex; + int mTabSize; bool mOverwrite; bool mReadOnly; bool mWithinRender; bool mScrollToCursor; + 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; + std::function mBreakpointsModifiedCallback; + std::function mGetGlobalValueCallback; + std::function mGetLocalValueCallback; + std::function mGetUpValueCallback; ErrorMarkers mErrorMarkers; ImVec2 mCharAdvance; Coordinates mInteractiveStart, mInteractiveEnd; + + Variables mVariables; };