diff --git a/bin/assets/plugins/lspclient.json b/bin/assets/plugins/lspclient.json index f04e78707..416436ea2 100644 --- a/bin/assets/plugins/lspclient.json +++ b/bin/assets/plugins/lspclient.json @@ -26,7 +26,9 @@ "includeCompletionsWithSnippetText": false, "jsxAttributeCompletionStyle": "auto", "providePrefixAndSuffixTextForRename": true, - "provideRefactorNotApplicableReason": true + "provideRefactorNotApplicableReason": true, + "importModuleSpecifierPreference": "shortest", + "disableSuggestions": false } } }, diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index 6af48edac..333af2e2c 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -360,7 +360,7 @@ class EE_API TextDocument { TextRanges findAll( const String& text, bool caseSensitive = true, bool wholeWord = false, const FindReplaceType& type = FindReplaceType::Normal, - TextRange restrictRange = TextRange() ); + TextRange restrictRange = TextRange(), size_t maxResults = 0 ); int replaceAll( const String& text, const String& replace, const bool& caseSensitive = true, const bool& wholeWord = false, @@ -569,6 +569,8 @@ class EE_API TextDocument { const Uint64& getModificationId() const; + void stopActiveFindAll(); + protected: friend class UndoStack; @@ -613,6 +615,8 @@ class EE_API TextDocument { mutable Mutex mLoadingFilePathMutex; size_t mLastSelection{ 0 }; std::unique_ptr mHighlighter; + Mutex mStopFlagsMutex; + std::unordered_map> mStopFlags; void initializeCommands(); @@ -694,7 +698,7 @@ struct TextSearchParams { bool operator!=( const TextSearchParams& other ) { return !( *this == other ); } - bool isEmpty() { return text.empty(); } + bool isEmpty(); void reset() { range = TextRange(); diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index d6028f2a4..e1391c0d8 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -642,7 +642,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { bool mFindReplaceEnabled{ true }; bool mShowIndentationGuides{ false }; bool mShowLinesRelativePosition{ false }; - std::atomic mHighlightWordProcessing{ false }; + std::atomic mHighlightWordProcessing{ false }; TextRange mLinkPosition; String mLink; Uint32 mTabWidth; @@ -683,6 +683,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { Clock mLongestLineWidthLastUpdate; TextSearchParams mHighlightWord; TextRanges mHighlightWordCache; + Mutex mHighlightWordCacheMutex; TextRange mHighlightTextRange; Color mPreviewColor; TextRange mPreviewColorRange; diff --git a/include/eepp/ui/uimenu.hpp b/include/eepp/ui/uimenu.hpp index ed9f292fc..252ecfe0f 100644 --- a/include/eepp/ui/uimenu.hpp +++ b/include/eepp/ui/uimenu.hpp @@ -155,7 +155,7 @@ class EE_API UIMenu : public UIWidget { virtual void scheduledUpdate( const Time& time ); - bool isChildOfMeOrSubMenu( Node* node ); + bool isChildOrSubMenu( Node* node ); void unselectSelected(); }; diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index 4f8a7ca64..9bd86e05e 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -41,6 +41,12 @@ TextDocument::TextDocument( bool verbose ) : } TextDocument::~TextDocument() { + stopActiveFindAll(); + + // TODO: Use a condition variable to wait the thread pool to finish + while ( !mStopFlags.empty() ) + Sys::sleep( Milliseconds( 0.1 ) ); + if ( mLoading ) { mLoading = false; Lock l( mLoadingMutex ); @@ -2351,11 +2357,25 @@ TextRange TextDocument::findLast( const String& text, TextPosition from, bool ca return TextRange(); } +void TextDocument::stopActiveFindAll() { + Lock l( mStopFlagsMutex ); + for ( const auto& stopFlag : mStopFlags ) + *stopFlag.second.get() = true; +} + TextRanges TextDocument::findAll( const String& text, bool caseSensitive, bool wholeWord, - const FindReplaceType& type, TextRange restrictRange ) { + const FindReplaceType& type, TextRange restrictRange, + size_t maxResults ) { TextRanges all; TextRange found; TextPosition from = startOfDoc(); + auto stopFlagUP = std::make_unique( false ); + bool* stopFlag = stopFlagUP.get(); + { + Lock l( mStopFlagsMutex ); + mStopFlags.insert( { stopFlag, std::move( stopFlagUP ) } ); + } + if ( restrictRange.isValid() ) from = restrictRange.normalized().start(); do { @@ -2365,10 +2385,17 @@ TextRanges TextDocument::findAll( const String& text, bool caseSensitive, bool w break; from = found.end(); all.push_back( found ); + if ( ( maxResults != 0 && all.size() >= maxResults ) || *stopFlag ) + break; } } while ( found.isValid() ); if ( !all.empty() ) all.setSorted(); + + { + Lock l( mStopFlagsMutex ); + mStopFlags.erase( stopFlag ); + } return all; } @@ -2733,4 +2760,8 @@ void TextDocument::addCursorBelow() { TextDocument::Client::~Client() {} +bool TextSearchParams::isEmpty() { + return text.empty(); +} + }}} // namespace EE::UI::Doc diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 366159051..4c79d8510 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -178,9 +178,13 @@ UICodeEditor::~UICodeEditor() { for ( auto& plugin : mPlugins ) plugin->onUnregister( this ); + // Remember to stop all the async find jobs + mDoc->stopActiveFindAll(); + // TODO: Use a condition variable to wait the thread pool to finish + // Wait to end all the async find jobs while ( mHighlightWordProcessing ) - Sys::sleep( Milliseconds( 1 ) ); + Sys::sleep( Milliseconds( 0.1 ) ); if ( mDoc.use_count() == 1 ) { DocEvent event( this, mDoc.get(), Event::OnDocumentClosed ); @@ -294,8 +298,10 @@ void UICodeEditor::draw() { } } - if ( !mHighlightWord.isEmpty() ) + if ( !mHighlightWord.isEmpty() ) { + Lock l( mHighlightWordCacheMutex ); drawWordRanges( mHighlightWordCache, lineRange, startScroll, lineHeight, true ); + } if ( mShowIndentationGuides ) { drawIndentationGuides( lineRange, startScroll, lineHeight ); @@ -2543,21 +2549,29 @@ void UICodeEditor::updateHighlightWordCache() { if ( mDoc->isRunningTransaction() ) return; Clock docSearch; - mHighlightWordProcessing = true; - mHighlightWordCache = mDoc->findAll( + mHighlightWordProcessing++; + mDoc->stopActiveFindAll(); + + auto wordCache = mDoc->findAll( mHighlightWord.escapeSequences ? String::unescape( mHighlightWord.text ) : mHighlightWord.text, mHighlightWord.caseSensitive, mHighlightWord.wholeWord, mHighlightWord.type, mHighlightWord.range ); + + { + Lock l( mHighlightWordCacheMutex ); + mHighlightWordCache = std::move( wordCache ); + } + Log::info( "Document search triggered in document: \"%s\", searched for " "\"%s\" and took %.2f ms", mDoc->getFilename().c_str(), mHighlightWord.text.toUtf8().c_str(), docSearch.getElapsedTime().asMilliseconds() ); }, - [this]( const auto& ) { mHighlightWordProcessing = false; }, tag ); + [this]( const auto& ) { mHighlightWordProcessing--; }, tag ); }, - Milliseconds( 0 ), tag ); + Milliseconds( 16 ), tag ); } else { if ( mDoc->isRunningTransaction() ) return; @@ -2565,7 +2579,7 @@ void UICodeEditor::updateHighlightWordCache() { mDoc->findAll( mHighlightWord.escapeSequences ? String::unescape( mHighlightWord.text ) : mHighlightWord.text, mHighlightWord.caseSensitive, mHighlightWord.wholeWord, - mHighlightWord.type, mHighlightWord.range ); + mHighlightWord.type, mHighlightWord.range, 100 ); } } @@ -3433,7 +3447,7 @@ void UICodeEditor::drawMinimap( const Vector2f& start, auto drawWordRanges = [&]( const TextRanges& ranges ) { primitives.setColor( Color( mMinimapHighlightColor ).blendAlpha( mAlpha ) ); - + Int64 lineSkip = -1; for ( const auto& range : ranges ) { if ( !( range.start().line() >= minimapStartLine && range.end().line() <= endidx ) || !range.inSameLine() ) @@ -3442,12 +3456,18 @@ void UICodeEditor::drawMinimap( const Vector2f& start, if ( ranges.isSorted() && range.end().line() > endidx ) break; + if ( lineSkip == range.start().line() ) + continue; + Rectf selRect; selRect.Top = rect.Top + ( range.start().line() - minimapStartLine ) * lineSpacing; selRect.Bottom = selRect.Top + charHeight; selRect.Left = minimapStart + getXOffsetCol( range.start() ) * widthScale; selRect.Right = minimapStart + getXOffsetCol( range.end() ) * widthScale; primitives.drawRectangle( selRect ); + + if ( selRect.Left > minimapCutoffX ) + lineSkip = range.start().line(); } }; @@ -3469,8 +3489,10 @@ void UICodeEditor::drawMinimap( const Vector2f& start, } } - if ( !mHighlightWord.isEmpty() ) + if ( !mHighlightWord.isEmpty() ) { + Lock l( mHighlightWordCacheMutex ); drawWordRanges( mHighlightWordCache ); + } if ( mMinimapConfig.syntaxHighlight ) { for ( int index = minimapStartLine; index <= endidx; index++ ) { diff --git a/src/eepp/ui/uimenu.cpp b/src/eepp/ui/uimenu.cpp index db84a4589..5a8a890d3 100644 --- a/src/eepp/ui/uimenu.cpp +++ b/src/eepp/ui/uimenu.cpp @@ -669,15 +669,15 @@ void UIMenu::scheduledUpdate( const Time& ) { if ( !mVisible ) return; Node* node = getEventDispatcher()->getMouseOverNode(); - if ( node && ( isChildOfMeOrSubMenu( node ) || getItemSelected() ) ) + if ( node && ( isChildOrSubMenu( node ) || getItemSelected() ) ) mInactiveTime.restart(); if ( mInactiveTime.getElapsedTime() > Seconds( 1 ) ) hide(); } -bool UIMenu::isChildOfMeOrSubMenu( Node* node ) { +bool UIMenu::isChildOrSubMenu( Node* node ) { return isParentOf( node ) || mOwnerNode == node || - ( mCurrentSubMenu && mCurrentSubMenu->isChildOfMeOrSubMenu( node ) ); + ( mCurrentSubMenu && mCurrentSubMenu->isChildOrSubMenu( node ) ); } void UIMenu::findBestMenuPos( Vector2f& pos, UIMenu* menu, UIMenu* parent, diff --git a/src/tools/uieditor/uieditor.cpp b/src/tools/uieditor/uieditor.cpp index e6275bb3d..a396f9d5b 100644 --- a/src/tools/uieditor/uieditor.cpp +++ b/src/tools/uieditor/uieditor.cpp @@ -1212,6 +1212,8 @@ void App::init( const Float& pixelDensityConf, const bool& useAppTheme, const st ContextSettings( false, GLv_default, true, 24, 1, 0, true ) ); if ( mWindow->isOpen() ) { + mWindow->setFrameRateLimit( displayManager->getDisplayIndex( 0 )->getRefreshRate() ); + PixelDensity::setPixelDensity( eemax( mWindow->getScale(), pixelDensity ) ); mWindow->setCloseRequestCallback(