From 775b65ffa6a7b6141b710a1a3bbe502a5da8e394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 13 Mar 2022 14:57:09 -0300 Subject: [PATCH] Some performance optimizations. --- bin/assets/formatter/formatter.json | 19 +++-- bin/assets/linters/linters.json | 6 ++ bin/assets/ui/breeze.css | 3 +- include/eepp/graphics/text.hpp | 8 ++ include/eepp/ui/uicodeeditor.hpp | 3 + projects/linux/ee.files | 1 + src/eepp/graphics/text.cpp | 18 ++++- src/eepp/ui/doc/syntaxdefinitionmanager.cpp | 85 +++++++++++++++++---- src/eepp/ui/doc/syntaxtokenizer.cpp | 15 +++- src/eepp/ui/uicodeeditor.cpp | 46 +++++++++-- src/tools/codeeditor/codeeditor.cpp | 16 ++-- 11 files changed, 185 insertions(+), 35 deletions(-) diff --git a/bin/assets/formatter/formatter.json b/bin/assets/formatter/formatter.json index 3ed6733ac..de4fa5732 100644 --- a/bin/assets/formatter/formatter.json +++ b/bin/assets/formatter/formatter.json @@ -1,15 +1,20 @@ [ { - "file_patterns": ["%.js$", "%.ts$"], - "command": "prettier $FILENAME" + "file_patterns": ["%.js$", "%.ts$"], + "command": "prettier $FILENAME" }, { - "file_patterns": ["%.cpp$", "%.h$", "%.hpp$"], - "command": "clang-format --style=file $FILENAME" + "file_patterns": ["%.cpp$", "%.h$", "%.hpp$"], + "command": "clang-format --style=file $FILENAME" }, { - "file_patterns": ["%.py$", "%.pyw$"], - "command": "black $FILENAME", - "type": "inplace" + "file_patterns": ["%.py$", "%.pyw$"], + "command": "black $FILENAME", + "type": "inplace" + }, + { + "file_patterns": ["%.kt$"], + "command": "ktlint -F $FILENAME", + "type": "inplace" } ] diff --git a/bin/assets/linters/linters.json b/bin/assets/linters/linters.json index 72a909e9a..2fdd408ff 100644 --- a/bin/assets/linters/linters.json +++ b/bin/assets/linters/linters.json @@ -38,5 +38,11 @@ "warning_pattern": "[^:]:(%d+):(%d+):%s?([^%s]*)([^\n]*)", "warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 }, "command": "cppcheck --language=c++ --enable=all --template=gcc $FILENAME" + }, + { + "file_patterns": ["%.kt$"], + "warning_pattern": "[^:]:(%d+):(%d+):%s([^\n]+)", + "warning_pattern_order": { "line": 1, "col": 2, "message": 3, "type": 4 }, + "command": "ktlint $FILENAME" } ] diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index a25630ad9..e2ef57e3b 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -492,7 +492,8 @@ Loader { CodeEditor > Loader { background-color: #0000002d; - radius: 64dp; + radius: 32dp; + outline-thickness: 6dp; } Window::close { diff --git a/include/eepp/graphics/text.hpp b/include/eepp/graphics/text.hpp index 9011e4bbc..9b5a3ccdc 100644 --- a/include/eepp/graphics/text.hpp +++ b/include/eepp/graphics/text.hpp @@ -150,6 +150,13 @@ class EE_API Text { /** Sets text background color. */ void setBackgroundColor( const Color& backgroundColor ); + /** @return True if the text width cache is disabled. */ + bool getDisableCacheWidth() const; + + /** The text width is cached every time the geometry of the text is updated. It's possible to + * disable this to improve performance in very specific scenarios. */ + void setDisableCacheWidth( bool newDisableCacheWidth ); + protected: struct VertexCoords { Vector2f texCoords; @@ -171,6 +178,7 @@ class EE_API Text { mutable bool mCachedWidthNeedUpdate; mutable bool mColorsNeedUpdate; mutable bool mContainsColorEmoji{ false }; + bool mDisableCacheWidth{ false }; Float mCachedWidth; int mNumLines; diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 66c755a5e..2e348048c 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -462,6 +462,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { TextRange mPreviewColorRange; std::vector mModules; UILoader* mLoader{ nullptr }; + Float mGlyphWidth{ 0 }; UICodeEditor( const std::string& elementTag, const bool& autoRegisterBaseCommands = true, const bool& autoRegisterBaseKeybindings = true ); @@ -595,6 +596,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void disableEditorFeatures(); Float getViewportWidth( const bool& forceVScroll = false ) const; + + void udpateGlyphWidth(); }; }} // namespace EE::UI diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 35e8eba60..d1a1fa11f 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -2,6 +2,7 @@ ../../TODO.md ../../bin/assets/colorschemes/colorschemes.conf ../../bin/assets/ee.ini +../../bin/assets/formatter/formatter.json ../../bin/assets/layouts/imported.css ../../bin/assets/layouts/test.css ../../bin/assets/layouts/test.xml diff --git a/src/eepp/graphics/text.cpp b/src/eepp/graphics/text.cpp index 074cd2639..23f55fa6c 100644 --- a/src/eepp/graphics/text.cpp +++ b/src/eepp/graphics/text.cpp @@ -533,6 +533,14 @@ void Text::setBackgroundColor( const Color& backgroundColor ) { mBackgroundColor = backgroundColor; } +bool Text::getDisableCacheWidth() const { + return mDisableCacheWidth; +} + +void Text::setDisableCacheWidth( bool newDisableCacheWidth ) { + mDisableCacheWidth = newDisableCacheWidth; +} + Rectf Text::getLocalBounds() { ensureGeometryUpdate(); @@ -684,7 +692,8 @@ void Text::draw( const Float& X, const Float& Y, const Vector2f& scale, const Fl } void Text::ensureGeometryUpdate() { - cacheWidth(); + if ( !mDisableCacheWidth ) + cacheWidth(); // Do nothing, if geometry has not changed if ( !mGeometryNeedUpdate ) @@ -714,8 +723,11 @@ void Text::ensureGeometryUpdate() { // Compute the location of the strike through dynamically // We use the center point of the lowercase 'x' glyph as the reference // We reuse the underline thickness as the thickness of the strike through as well - Rectf xBounds = mFont->getGlyph( L'x', mRealFontSize, bold ).bounds; - Float strikeThroughOffset = xBounds.Top + xBounds.Bottom / 2.f; + Float strikeThroughOffset = 0; + if ( strikeThrough ) { + Rectf xBounds = mFont->getGlyph( L'x', mRealFontSize, bold ).bounds; + strikeThroughOffset = xBounds.Top + xBounds.Bottom / 2.f; + } // Precompute the variables needed by the algorithm Float hspace = static_cast( mFont->getGlyph( L' ', mRealFontSize, bold ).advance ); diff --git a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp index f90a7e980..f59e38e05 100644 --- a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp +++ b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp @@ -920,19 +920,20 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { // ini / conf add( { "Config File", { "%.ini$", "%.conf$", "%.desktop$", "%.service$", "%.cfg$", "Doxyfile" }, - { - { { "^#.-\n" }, "comment" }, - { { "%s#.-\n" }, "comment" }, - { { "%s?#%x+" }, "string" }, - { { "[%a_][%w-+_%s%p]*%f[=]" }, "keyword" }, - { { "\"", "\"", "\\" }, "string" }, - { { "'", "'", "\\" }, "string" }, - { { "^%[.-%]" }, "keyword2" }, - { { "%s%[.-%]" }, "keyword2" }, - { { "=" }, "operator" }, - { { "https?://%S+" }, "link" }, - }, - {}, + { { { "^#.-\n" }, "comment" }, + { { "%s#.-\n" }, "comment" }, + { { "%s?#%x+" }, "string" }, + { { "[%a_][%w-+_%s%p]*%f[=]" }, "keyword" }, + { { "\"", "\"", "\\" }, "string" }, + { { "'", "'", "\\" }, "string" }, + { { "^%[.-%]" }, "keyword2" }, + { { "%s%[.-%]" }, "keyword2" }, + { { "=" }, "operator" }, + { { "https?://(([%w_.~!*:@&+$/?%%#-]-)(%w[-.%w]*%.)(%w%w%w?%w?)(:?)(%d*)(/" + "?)([%w_.~!*:@&+$/?%%#=-]*))" }, + "link" }, + { { "[a-z]+" }, "symbol" } }, + { { "true", "literal" }, { "false", "literal" } }, "#", { "^%[.-%]" } } ); @@ -2178,6 +2179,64 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { { "switch", "keyword" }, { "then", "keyword" }, { "this", "keyword2" }, { "true", "literal" }, { "void", "keyword" }, { "while", "keyword" } }, "//" } ); + + // Add Kotlin + add( { "Kotlin", + { "%.kt$" }, + { + { { "//.-\n" }, "comment" }, + { { "/%*", "%*/" }, "comment" }, + { { "\"", "\"", "\\" }, "string" }, + { { "'", "'", "\\" }, "string" }, + { { "'\\x%x?%x?%x?%x'" }, "string" }, + { { "'\\u%x%x%x%x'" }, "string" }, + { { "'\\?.'" }, "string" }, + { { "-?0x%x+" }, "number" }, + { { "-?%d+[%d%.eE]*f?" }, "number" }, + { { "-?%.?%d+f?" }, "number" }, + { { "[%+%-=/%*%^%%<>!~|&]" }, "operator" }, + { { "%@[%a_][%w_]*" }, "function" }, + { { "[%a_][%w_]*%f[(]" }, "function" }, + { { "[%a_][%w_]*" }, "symbol" }, + }, + { { "if", "keyword" }, { "then", "keyword" }, + { "else", "keyword" }, { "elseif", "keyword" }, + { "do", "keyword" }, { "while", "keyword" }, + { "for", "keyword" }, { "new", "keyword" }, + { "break", "keyword" }, { "continue", "keyword" }, + { "return", "keyword" }, { "goto", "keyword" }, + { "class", "keyword" }, { "implements", "keyword" }, + { "extends", "keyword" }, { "private", "keyword" }, + { "protected", "keyword" }, { "public", "keyword" }, + { "abstract", "keyword" }, { "interface", "keyword" }, + { "assert", "keyword" }, { "import", "keyword" }, + { "native", "keyword" }, { "package", "keyword" }, + { "super", "keyword" }, { "synchronized", "keyword" }, + { "instanceof", "keyword" }, { "enum", "keyword" }, + { "catch", "keyword" }, { "throw", "keyword" }, + { "throws", "keyword" }, { "try", "keyword" }, + { "transient", "keyword" }, { "finally", "keyword" }, + { "static", "keyword" }, { "volatile", "keyword" }, + { "final", "keyword" }, { "switch", "keyword" }, + { "case", "keyword" }, { "default", "keyword" }, + { "void", "keyword" }, { "Int", "keyword2" }, + { "Short", "keyword2" }, { "Byte", "keyword2" }, + { "Long", "keyword2" }, { "Float", "keyword2" }, + { "Double", "keyword2" }, { "String", "keyword2" }, + { "Boolean", "keyword2" }, { "true", "literal" }, + { "false", "literal" }, { "null", "literal" }, + { "var", "keyword" }, { "lateinit", "literal" }, + { "override", "keyword" }, { "fun", "keyword" }, + { "sealed", "keyword" }, { "companion", "keyword" }, + { "object", "keyword" }, { "val", "keyword" }, + { "UInt", "keyword2" }, { "UShort", "keyword2" }, + { "UByte", "keyword2" }, { "ULong", "keyword2" }, + { "IntArray", "keyword2" }, { "ShortArray", "keyword2" }, + { "ByteArray", "keyword2" }, { "LongArray", "keyword2" }, + { "UIntArray", "keyword2" }, { "UShortArray", "keyword2" }, + { "UByteArray", "keyword2" }, { "ULongArray", "keyword2" }, + { "Array", "keyword2" } }, + "//" } ); } SyntaxDefinition& SyntaxDefinitionManager::add( SyntaxDefinition&& syntaxStyle ) { diff --git a/src/eepp/ui/doc/syntaxtokenizer.cpp b/src/eepp/ui/doc/syntaxtokenizer.cpp index 649bac95c..0520656bb 100644 --- a/src/eepp/ui/doc/syntaxtokenizer.cpp +++ b/src/eepp/ui/doc/syntaxtokenizer.cpp @@ -17,6 +17,8 @@ namespace EE { namespace UI { namespace Doc { return true; }*/ +#define MAX_TOKEN_SIZE ( 512 ) + static void pushToken( std::vector& tokens, const std::string& type, const std::string& text ) { if ( !tokens.empty() && ( tokens[tokens.size() - 1].type == type /*|| @@ -24,7 +26,18 @@ static void pushToken( std::vector& tokens, const std::string& type tokens[tokens.size() - 1].type = type; tokens[tokens.size() - 1].text += text; } else { - tokens.push_back( {type, text} ); + if ( text.size() > MAX_TOKEN_SIZE ) { + size_t textSize = text.size(); + size_t steps = textSize / MAX_TOKEN_SIZE + 1; + + for ( size_t i = 0; i < steps; ++i ) { + size_t strSize = + ( i == steps - 1 ) ? textSize - MAX_TOKEN_SIZE * i : MAX_TOKEN_SIZE; + tokens.push_back( { type, text.substr( i * MAX_TOKEN_SIZE, strSize ) } ); + } + } else { + tokens.push_back( { type, text } ); + } } } diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 1ab28f21d..59035426e 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -423,9 +423,13 @@ UICodeEditor* UICodeEditor::setFont( Font* font ) { return this; } -void UICodeEditor::onFontChanged() {} +void UICodeEditor::onFontChanged() { + udpateGlyphWidth(); +} -void UICodeEditor::onFontStyleChanged() {} +void UICodeEditor::onFontStyleChanged() { + udpateGlyphWidth(); +} void UICodeEditor::onDocumentChanged() { DocEvent event( this, mDoc.get(), Event::OnDocumentChanged ); @@ -462,6 +466,7 @@ UICodeEditor* UICodeEditor::setFontSize( const Float& dpSize ) { eeabs( dpSize - (int)dpSize ) == 0.5f || (int)dpSize == dpSize ? dpSize : eefloor( dpSize ); mFontSize = mFontStyleConfig.CharacterSize; + udpateGlyphWidth(); invalidateDraw(); onFontChanged(); } @@ -1634,7 +1639,11 @@ Float UICodeEditor::getCharacterSize() const { } Float UICodeEditor::getGlyphWidth() const { - return mFont->getGlyph( ' ', getCharacterSize(), false ).advance; + return mGlyphWidth; +} + +void UICodeEditor::udpateGlyphWidth() { + mGlyphWidth = mFont->getGlyph( ' ', getCharacterSize(), false ).advance; } const bool& UICodeEditor::getColorPreview() const { @@ -1879,12 +1888,18 @@ void UICodeEditor::drawLineText( const Int64& index, Vector2f position, const Fl const Float& lineHeight ) { auto& tokens = mHighlighter.getLine( index ); Primitives primitives; + Int64 curChar = 0; + Int64 maxWidth = eeceil( mSize.getWidth() / getGlyphWidth() + 1 ); for ( auto& token : tokens ) { String text( token.text ); Float textWidth = getTextWidth( text ); if ( position.x + textWidth >= mScreenPos.x && position.x <= mScreenPos.x + mSize.getWidth() ) { + Int64 curCharsWidth = text.size(); + Int64 curPositionChar = eefloor( mScroll.x / getGlyphWidth() ); + Float curMaxPositionChar = curPositionChar + maxWidth; Text line( "", mFont, fontSize ); + line.setDisableCacheWidth( true ); line.setTabWidth( mTabWidth ); const SyntaxColorScheme::Style& style = mColorScheme.getSyntaxStyle( token.type ); line.setStyleConfig( mFontStyleConfig ); @@ -1895,12 +1910,33 @@ void UICodeEditor::drawLineText( const Int64& index, Vector2f position, const Fl primitives.drawRectangle( Rectf( position, Sizef( textWidth, lineHeight ) ) ); } line.setColor( Color( style.color ).blendAlpha( mAlpha ) ); - line.setString( text ); - line.draw( position.x, position.y ); + if ( curPositionChar + curChar + curCharsWidth > curMaxPositionChar ) { + if ( curChar < curPositionChar ) { + Int64 charsToVisible = curPositionChar - curChar; + Int64 start = eemax( (Int64)0, curPositionChar - curChar ); + Int64 minimumCharsToCoverScreen = maxWidth + charsToVisible - start; + Int64 totalChars = curCharsWidth - start; + Int64 end = eemin( totalChars, minimumCharsToCoverScreen ); + if ( curCharsWidth >= charsToVisible ) { + line.setString( text.substr( start, end ) ); + line.draw( position.x + start * getGlyphWidth(), position.y ); + if ( minimumCharsToCoverScreen == end ) + break; + } + } else { + line.setString( text.substr( 0, eemin( curCharsWidth, maxWidth ) ) ); + line.draw( position.x, position.y ); + } + } else { + line.setString( text ); + line.draw( position.x, position.y ); + } } else if ( position.x > mScreenPos.x + mSize.getWidth() ) { break; } + position.x += textWidth; + curChar += text.size(); } } diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index 120ffb30b..f2dbb4e8c 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -116,11 +116,17 @@ void App::updateEditorTitle( UICodeEditor* editor ) { } void App::setAppTitle( const std::string& title ) { - mWindow->setTitle( mWindowTitle + - String( mCurrentProject.empty() - ? "" - : " - " + FileSystem::fileNameFromPath( mCurrentProject ) ) + - String( title.empty() ? "" : " - " + title ) ); + std::string fullTitle( mWindowTitle ); + if ( !mCurrentProject.empty() ) { + std::string currentProject( FileSystem::fileNameFromPath( mCurrentProject ) ); + if ( !currentProject.empty() ) + fullTitle += " - " + currentProject; + } + + if ( !title.empty() ) + fullTitle += " - " + title; + + mWindow->setTitle( fullTitle ); } void App::onDocumentModified( UICodeEditor* editor, TextDocument& ) {