From 8722518986b4dab7dd8eb4f1f350384360cf19c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 10 Nov 2022 11:42:42 -0300 Subject: [PATCH] Auto-complete module improvements. --- bin/assets/colorschemes/colorschemes.conf | 1 + src/eepp/ui/doc/syntaxcolorscheme.cpp | 3 + .../autocomplete/autocompleteplugin.cpp | 208 ++++++++++++------ .../autocomplete/autocompleteplugin.hpp | 17 +- .../ecode/plugins/linter/linterplugin.cpp | 1 + .../ecode/plugins/lsp/lspclientplugin.cpp | 1 + .../plugins/lsp/lspclientservermanager.cpp | 4 +- src/tools/ecode/plugins/pluginmanager.cpp | 3 +- 8 files changed, 168 insertions(+), 70 deletions(-) diff --git a/bin/assets/colorschemes/colorschemes.conf b/bin/assets/colorschemes/colorschemes.conf index 195e6f429..50ad04d47 100644 --- a/bin/assets/colorschemes/colorschemes.conf +++ b/bin/assets/colorschemes/colorschemes.conf @@ -14,6 +14,7 @@ matching_selection = #3e596e matching_search = #181b1e suggestion = #e1e1e6,#1d1f27 suggestion_selected = #ffffff,#2f3240 +suggestion_scrollbar = #3daee9 error = red warning = yellow notice = #8abdff diff --git a/src/eepp/ui/doc/syntaxcolorscheme.cpp b/src/eepp/ui/doc/syntaxcolorscheme.cpp index 0173354fc..5e015b920 100644 --- a/src/eepp/ui/doc/syntaxcolorscheme.cpp +++ b/src/eepp/ui/doc/syntaxcolorscheme.cpp @@ -66,6 +66,7 @@ SyntaxColorScheme SyntaxColorScheme::getDefault() { { "matching_selection", Color( "#3e596e" ) }, { "matching_search", Color( "#181b1e" ) }, { "suggestion", { Color( "#e1e1e6" ), Color( "#1d1f27" ), Text::Regular } }, + { "suggestion_scrollbar", { Color( "#3daee9" ) } }, { "suggestion_selected", { Color( "#ffffff" ), Color( "#2f3240" ), Text::Regular } }, { "error", { Color::Red } }, { "warning", { Color::Yellow } }, @@ -263,6 +264,8 @@ SyntaxColorScheme::getEditorSyntaxStyle( const std::string& type ) const { return StyleDefault.getEditorSyntaxStyle( "minimap_highlight" ); else if ( type == "minimap_visible_area" ) return StyleDefault.getEditorSyntaxStyle( "minimap_visible_area" ); + else if ( type == "suggestion_scrollbar" ) + return getEditorSyntaxStyle( "line_highlight" ); return StyleEmpty; } diff --git a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp index e9935e01b..db2b2585f 100644 --- a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp +++ b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp @@ -18,6 +18,29 @@ namespace ecode { #define AUTO_COMPLETE_THREADED 0 #endif +static std::vector +fuzzyMatchSymbols( const std::vector& symbolsVec, + const std::string& match, const size_t& max ) { + std::multimap> matchesMap; + std::vector matches; + int score; + for ( const auto& symbols : symbolsVec ) { + for ( const auto& symbol : *symbols ) { + if ( ( score = String::fuzzyMatch( symbol, match ) ) > 0 ) { + matchesMap.insert( { score, symbol } ); + } + } + } + std::string prevMatch; + for ( auto& res : matchesMap ) { + if ( matches.size() < max ) { + if ( std::find( matches.begin(), matches.end(), res.second ) == matches.end() ) + matches.emplace_back( res.second ); + } + } + return matches; +} + UICodeEditorPlugin* AutoCompletePlugin::New( const PluginManager* pluginManager ) { return eeNew( AutoCompletePlugin, ( pluginManager ) ); } @@ -35,6 +58,7 @@ AutoCompletePlugin::AutoCompletePlugin( const PluginManager* pluginManager ) : AutoCompletePlugin::~AutoCompletePlugin() { mClosing = true; + mManager->unsubscribeMessages( this ); Lock l( mDocMutex ); Lock l2( mLangSymbolsMutex ); Lock l3( mSuggestionsMutex ); @@ -127,21 +151,31 @@ void AutoCompletePlugin::onUnregister( UICodeEditor* editor ) { bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event ) { if ( !mSuggestions.empty() ) { - int max = eemin( mSuggestionsMaxVisible, mSuggestions.size() ); if ( event.getKeyCode() == KEY_DOWN ) { - if ( mSuggestionIndex + 1 < max ) { - mSuggestionIndex += 1; + if ( mSuggestionIndex + 1 < (int)mSuggestions.size() ) { + mSuggestionIndex++; + if ( mSuggestionIndex < mSuggestionsStartIndex ) + mSuggestionsStartIndex = mSuggestionIndex; + else if ( mSuggestionIndex > mSuggestionsStartIndex + mSuggestionsMaxVisible - 1 ) { + mSuggestionsStartIndex = + eemax( 0, mSuggestionIndex - ( mSuggestionsMaxVisible - 1 ) ); + } } else { mSuggestionIndex = 0; + mSuggestionsStartIndex = 0; } editor->invalidateDraw(); return true; } else if ( event.getKeyCode() == KEY_UP ) { if ( mSuggestionIndex - 1 < 0 ) { - mSuggestionIndex = max - 1; + mSuggestionIndex = mSuggestions.size() - 1; + mSuggestionsStartIndex = + eemax( 0, (int)mSuggestions.size() - mSuggestionsMaxVisible ); } else { - mSuggestionIndex -= 1; + mSuggestionIndex--; } + if ( mSuggestionIndex < (int)mSuggestionsStartIndex ) + mSuggestionsStartIndex = mSuggestionIndex; editor->invalidateDraw(); return true; } else if ( event.getKeyCode() == KEY_ESCAPE ) { @@ -150,19 +184,33 @@ bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event return true; } else if ( event.getKeyCode() == KEY_HOME ) { mSuggestionIndex = 0; + mSuggestionsStartIndex = 0; editor->invalidateDraw(); return true; } else if ( event.getKeyCode() == KEY_END ) { - mSuggestionIndex = max - 1; + mSuggestionIndex = mSuggestions.size() - 1; + mSuggestionsStartIndex = eemax( 0, (int)mSuggestions.size() - mSuggestionsMaxVisible ); editor->invalidateDraw(); return true; } else if ( event.getKeyCode() == KEY_PAGEUP ) { - mSuggestionIndex = eemax( mSuggestionIndex - (int)mSuggestionsMaxVisible, 0 ); + if ( mSuggestionIndex - (int)( mSuggestionsMaxVisible - 1 ) >= 0 ) { + mSuggestionIndex -= ( mSuggestionsMaxVisible - 1 ); + if ( mSuggestionIndex < mSuggestionsStartIndex ) + mSuggestionsStartIndex = mSuggestionIndex; + } else { + mSuggestionIndex = 0; + mSuggestionsStartIndex = 0; + } editor->invalidateDraw(); return true; } else if ( event.getKeyCode() == KEY_PAGEDOWN ) { - mSuggestionIndex = - eemin( mSuggestionIndex + (int)mSuggestionsMaxVisible, max - 1 ); + if ( mSuggestionIndex + mSuggestionsMaxVisible < (int)mSuggestions.size() ) { + mSuggestionIndex += mSuggestionsMaxVisible - 1; + } else { + mSuggestionIndex = mSuggestions.size() - 1; + } + mSuggestionsStartIndex = + eemax( 0, mSuggestionIndex - ( mSuggestionsMaxVisible - 1 ) ); editor->invalidateDraw(); return true; } else if ( event.getKeyCode() == KEY_TAB || event.getKeyCode() == KEY_RETURN || @@ -173,10 +221,8 @@ bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event } else if ( event.getKeyCode() == KEY_SPACE && ( event.getMod() & KeyMod::getDefaultModifier() ) ) { std::string partialSymbol( getPartialSymbol( &editor->getDocument() ) ); - if ( partialSymbol.size() >= 3 ) { - updateSuggestions( partialSymbol, editor ); - return true; - } + updateSuggestions( partialSymbol, editor ); + return true; } return false; } @@ -222,7 +268,7 @@ void AutoCompletePlugin::updateDocCache( TextDocument* doc ) { lang.clear(); for ( const auto& d : mDocCache ) { if ( d.first->getSyntaxDefinition().getLanguageName() == langName ) - lang.insert( d.second.symbols.begin(), d.second.symbols.end() ); + lang.insert( lang.end(), d.second.symbols.begin(), d.second.symbols.end() ); } } Log::debug( "Dictionary for %s updated in: %.2fms", doc->getFilename().c_str(), @@ -237,7 +283,7 @@ void AutoCompletePlugin::updateLangCache( const std::string& langName ) { lang.clear(); for ( const auto& d : mDocCache ) { if ( d.first->getSyntaxDefinition().getLanguageName() == langName ) - lang.insert( d.second.symbols.begin(), d.second.symbols.end() ); + lang.insert( lang.end(), d.second.symbols.begin(), d.second.symbols.end() ); } Log::debug( "Lang dictionary for %s updated in: %.2fms", langName.c_str(), clock.getElapsedTime().asMilliseconds() ); @@ -245,7 +291,9 @@ void AutoCompletePlugin::updateLangCache( const std::string& langName ) { void AutoCompletePlugin::pickSuggestion( UICodeEditor* editor ) { mReplacing = true; - editor->getDocument().execute( "delete-to-previous-word" ); + std::string symbol( getPartialSymbol( editor->getDocumentRef().get() ) ); + if ( !symbol.empty() ) + editor->getDocument().execute( "delete-to-previous-word" ); editor->getDocument().textInput( mSuggestions[mSuggestionIndex] ); mReplacing = false; resetSuggestions( editor ); @@ -254,19 +302,40 @@ void AutoCompletePlugin::pickSuggestion( UICodeEditor* editor ) { PluginRequestHandle AutoCompletePlugin::processResponse( const PluginMessage& msg ) { if ( msg.isResponse() && msg.type == PluginMessageType::CodeCompletion ) { auto completion = msg.asCodeCompletion(); - std::vector suggestions; + SymbolsList suggestions; for ( const auto& item : completion ) { if ( !item.textEdit.text.empty() ) suggestions.push_back( item.textEdit.text ); - else if ( !item.label.empty() ) - suggestions.push_back( item.label ); + else if ( !item.insertText.empty() ) + suggestions.push_back( item.insertText ); else suggestions.push_back( item.filterText ); } - if ( suggestions.empty() ) + if ( suggestions.empty() || !mSuggestionsEditor ) return {}; - Lock l( mSuggestionsMutex ); - mSuggestions = suggestions; + std::string symbol( getPartialSymbol( mSuggestionsEditor->getDocumentRef().get() ) ); + const std::string& lang = + mSuggestionsEditor->getDocument().getSyntaxDefinition().getLanguageName(); + Lock l2( mLangSymbolsMutex ); + auto langSuggestions = mLangCache.find( lang ); + std::vector fuzzySuggestions; + if ( symbol.empty() ) { + Lock l( mSuggestionsMutex ); + mSuggestions = suggestions; + } else { + if ( langSuggestions == mLangCache.end() ) { + fuzzySuggestions = fuzzyMatchSymbols( { &suggestions }, symbol, + eemax( 100UL, suggestions.size() ) ); + } else { + auto& symbols = langSuggestions->second; + fuzzySuggestions = fuzzyMatchSymbols( { &suggestions, &symbols }, symbol, + eemax( 100UL, suggestions.size() ) ); + } + Lock l( mSuggestionsMutex ); + mSuggestions = fuzzySuggestions; + } + + mSuggestionsEditor->runOnMainThread( [this] { mSuggestionsEditor->invalidateDraw(); } ); } else if ( msg.isBroadcast() && msg.type == PluginMessageType::LanguageServerCapabilities ) { if ( msg.asLanguageServerCapabilities().ready ) { LSPServerCapabilities cap = msg.asLanguageServerCapabilities(); @@ -277,15 +346,16 @@ PluginRequestHandle AutoCompletePlugin::processResponse( const PluginMessage& ms return {}; } -void AutoCompletePlugin::tryRequestCapabilities( UICodeEditor* editor ) { +bool AutoCompletePlugin::tryRequestCapabilities( UICodeEditor* editor ) { const auto& language = editor->getDocumentRef()->getSyntaxDefinition().getLSPName(); auto it = mCapabilities.find( language ); if ( it != mCapabilities.end() ) - return; + return true; json data; data["language"] = language; mManager->sendRequest( this, PluginMessageType::LanguageServerCapabilities, PluginMessageFormat::JSON, &data ); + return false; } std::string AutoCompletePlugin::getPartialSymbol( TextDocument* doc ) { @@ -332,16 +402,26 @@ void AutoCompletePlugin::postDraw( UICodeEditor* editor, const Vector2f& startSc mRowHeight = lineHeight + mBoxPadding.Top + mBoxPadding.Bottom; const auto& normalStyle = scheme.getEditorSyntaxStyle( "suggestion" ); const auto& selectedStyle = scheme.getEditorSyntaxStyle( "suggestion_selected" ); + const auto& barStyle = scheme.getEditorSyntaxStyle( "suggestion_scrollbar" ); if ( cursorPos.y + mRowHeight * max > editor->getPixelsSize().getHeight() ) cursorPos.y -= lineHeight + mRowHeight * max; - for ( size_t i = 0; i < max; i++ ) + + size_t maxIndex = + eemin( mSuggestionsStartIndex + mSuggestionsMaxVisible, suggestions.size() ); + + for ( size_t i = mSuggestionsStartIndex; i < maxIndex; i++ ) largestString = eemax( largestString, editor->getTextWidth( suggestions[i] ) ); - mBoxRect = - Rectf( Vector2f( cursorPos.x, cursorPos.y ) - editor->getScreenPos(), - Sizef( largestString + mBoxPadding.Left + mBoxPadding.Right, mRowHeight * max ) ); + Sizef bar( PixelDensity::dpToPx( 8 ), + mBoxRect.getSize().getHeight() * + ( mSuggestionsMaxVisible / (Float)suggestions.size() ) ); - for ( size_t i = 0; i < max; i++ ) { + mBoxRect = Rectf( Vector2f( cursorPos.x, cursorPos.y ) - editor->getScreenPos(), + Sizef( largestString + mBoxPadding.Left + mBoxPadding.Right + bar.getWidth(), + mRowHeight * max ) ); + + size_t count = 0; + for ( size_t i = mSuggestionsStartIndex; i < maxIndex; i++ ) { Text text( "", editor->getFont(), editor->getFontSize() ); text.setFillColor( mSuggestionIndex == (int)i ? selectedStyle.color : normalStyle.color ); text.setStyle( mSuggestionIndex == (int)i ? selectedStyle.style : normalStyle.style ); @@ -350,10 +430,26 @@ void AutoCompletePlugin::postDraw( UICodeEditor* editor, const Vector2f& startSc Color( mSuggestionIndex == (int)i ? selectedStyle.background : normalStyle.background ) .blendAlpha( editor->getAlpha() ) ); primitives.drawRectangle( - Rectf( Vector2f( cursorPos.x, cursorPos.y + mRowHeight * i ), - Sizef( largestString + mBoxPadding.Left + mBoxPadding.Right, mRowHeight ) ) ); - text.draw( cursorPos.x + mBoxPadding.Left, cursorPos.y + mRowHeight * i + mBoxPadding.Top ); + Rectf( Vector2f( cursorPos.x, cursorPos.y + mRowHeight * count ), + Sizef( largestString + mBoxPadding.Left + mBoxPadding.Right + bar.getWidth(), + mRowHeight ) ) ); + text.draw( cursorPos.x + mBoxPadding.Left, + cursorPos.y + mRowHeight * count + mBoxPadding.Top ); + count++; } + + if ( max >= suggestions.size() ) + return; + + primitives.setColor( barStyle.color ); + Float yPos = mSuggestionsStartIndex > 0 + ? mSuggestionsStartIndex / + (Float)( ( suggestions.size() - 1 ) - mSuggestionsMaxVisible ) + : 0; + primitives.drawRectangle( + { Vector2f( cursorPos.x + mBoxRect.getWidth() - bar.getWidth(), + cursorPos.y + ( mBoxRect.getHeight() - bar.getHeight() ) * yPos ), + bar } ); } bool AutoCompletePlugin::onMouseDown( UICodeEditor* editor, const Vector2i& position, @@ -419,7 +515,7 @@ void AutoCompletePlugin::setBoxPadding( const Rectf& boxPadding ) { mBoxPadding = boxPadding; } -const Uint32& AutoCompletePlugin::getSuggestionsMaxVisible() const { +const Int32& AutoCompletePlugin::getSuggestionsMaxVisible() const { return mSuggestionsMaxVisible; } @@ -454,6 +550,7 @@ void AutoCompletePlugin::setDirty( bool dirty ) { void AutoCompletePlugin::resetSuggestions( UICodeEditor* editor ) { Lock l( mSuggestionsMutex ); mSuggestionIndex = 0; + mSuggestionsStartIndex = 0; mSuggestionsEditor = nullptr; mSuggestions.clear(); if ( editor && editor->hasFocus() ) @@ -475,7 +572,7 @@ AutoCompletePlugin::SymbolsList AutoCompletePlugin::getDocumentSymbols( TextDocu // Ignore the symbol if is actually the current symbol being written if ( matchStr.size() < 3 || ( end.line() == i && current == matchStr ) ) continue; - symbols.insert( std::move( matchStr ) ); + symbols.push_back( std::move( matchStr ) ); } if ( mClosing ) break; @@ -483,40 +580,25 @@ AutoCompletePlugin::SymbolsList AutoCompletePlugin::getDocumentSymbols( TextDocu return symbols; } -static std::vector fuzzyMatchSymbols( const AutoCompletePlugin::SymbolsList& symbols, - const std::string& match, const size_t& max ) { - std::multimap> matchesMap; - std::vector matches; - int score; - for ( const auto& symbol : symbols ) { - if ( ( score = String::fuzzyMatch( symbol, match ) ) > 0 ) { - matchesMap.insert( { score, symbol } ); - } - } - for ( auto& res : matchesMap ) { - if ( matches.size() < max ) - matches.emplace_back( res.second ); - } - return matches; -} - void AutoCompletePlugin::runUpdateSuggestions( const std::string& symbol, const SymbolsList& symbols, UICodeEditor* editor ) { { - tryRequestCapabilities( editor ); - json data; - auto doc = editor->getDocumentRef(); - auto sel = doc->getSelection(); - data["uri"] = doc->getURI().toString(); - data["position"] = { { "line", sel.start().line() }, - { "character", sel.start().column() } }; - mManager->sendRequest( this, PluginMessageType::CodeCompletion, PluginMessageFormat::JSON, - &data ); - + mSuggestionsEditor = editor; + if ( tryRequestCapabilities( editor ) ) { + json data; + auto doc = editor->getDocumentRef(); + auto sel = doc->getSelection(); + data["uri"] = doc->getURI().toString(); + data["position"] = { { "line", sel.start().line() }, + { "character", sel.start().column() } }; + mManager->sendRequest( this, PluginMessageType::CodeCompletion, + PluginMessageFormat::JSON, &data ); + } + if ( symbol.empty() ) + return; Lock l( mLangSymbolsMutex ); Lock l2( mSuggestionsMutex ); - mSuggestions = fuzzyMatchSymbols( symbols, symbol, mSuggestionsMaxVisible ); - mSuggestionsEditor = editor; + mSuggestions = fuzzyMatchSymbols( { &symbols }, symbol, mSuggestionsMaxVisible ); } editor->runOnMainThread( [editor] { editor->invalidateDraw(); } ); } diff --git a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp index a90f24cf8..9377a8fb1 100644 --- a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp +++ b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp @@ -18,7 +18,7 @@ namespace ecode { class AutoCompletePlugin : public UICodeEditorPlugin { public: - typedef std::unordered_set SymbolsList; + typedef std::vector SymbolsList; static PluginDefinition Definition() { return { "autocomplete", @@ -57,7 +57,7 @@ class AutoCompletePlugin : public UICodeEditorPlugin { void setBoxPadding( const Rectf& boxPadding ); - const Uint32& getSuggestionsMaxVisible() const; + const Int32& getSuggestionsMaxVisible() const; void setSuggestionsMaxVisible( const Uint32& suggestionsMaxVisible ); @@ -74,6 +74,12 @@ class AutoCompletePlugin : public UICodeEditorPlugin { void setDirty( bool dirty ); protected: + struct Suggestion { + std::string text; + std::string desc; + std::string sortText; + TextRange range; + }; const PluginManager* mManager{ nullptr }; std::string mSymbolPattern; Rectf mBoxPadding; @@ -97,10 +103,11 @@ class AutoCompletePlugin : public UICodeEditorPlugin { std::unordered_map mLangCache; SymbolsList mLangDirty; - int mSuggestionIndex{ 0 }; std::vector mSuggestions; - Uint32 mSuggestionsMaxVisible{ 8 }; UICodeEditor* mSuggestionsEditor{ nullptr }; + Int32 mSuggestionIndex{ 0 }; + Int32 mSuggestionsMaxVisible{ 8 }; + Int32 mSuggestionsStartIndex{ 0 }; std::map mCapabilities; Mutex mCapabilitiesMutex; @@ -128,7 +135,7 @@ class AutoCompletePlugin : public UICodeEditorPlugin { PluginRequestHandle processResponse( const PluginMessage& msg ); - void tryRequestCapabilities( UICodeEditor* editor ); + bool tryRequestCapabilities( UICodeEditor* editor ); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/linter/linterplugin.cpp b/src/tools/ecode/plugins/linter/linterplugin.cpp index e5118a004..78012c7fe 100644 --- a/src/tools/ecode/plugins/linter/linterplugin.cpp +++ b/src/tools/ecode/plugins/linter/linterplugin.cpp @@ -37,6 +37,7 @@ LinterPlugin::LinterPlugin( const PluginManager* pluginManager ) : LinterPlugin::~LinterPlugin() { mShuttingDown = true; + mManager->unsubscribeMessages( this ); if ( mWorkersCount != 0 ) { std::unique_lock lock( mWorkMutex ); diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index 9df15a675..067d7d937 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -24,6 +24,7 @@ LSPClientPlugin::LSPClientPlugin( const PluginManager* pluginManager ) : LSPClientPlugin::~LSPClientPlugin() { mClosing = true; + mManager->unsubscribeMessages( this ); Lock l( mDocMutex ); for ( const auto& editor : mEditors ) { for ( auto& kb : mKeyBindings ) { diff --git a/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp b/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp index 0e52e6fd5..d934d4548 100644 --- a/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp @@ -155,9 +155,11 @@ void LSPClientServerManager::updateDirty() { mLSPsToClose.push_back( server.first ); } } - if ( !mLSPsToClose.empty() ) + if ( !mLSPsToClose.empty() ) { for ( const auto& server : mLSPsToClose ) closeLSPServer( server ); + mLSPsToClose.clear(); + } } void LSPClientServerManager::getAndGoToLocation( const std::shared_ptr& doc, diff --git a/src/tools/ecode/plugins/pluginmanager.cpp b/src/tools/ecode/plugins/pluginmanager.cpp index 09bb90cd8..8db2673c0 100644 --- a/src/tools/ecode/plugins/pluginmanager.cpp +++ b/src/tools/ecode/plugins/pluginmanager.cpp @@ -154,7 +154,8 @@ void PluginManager::subscribeMessages( } void PluginManager::unsubscribeMessages( UICodeEditorPlugin* plugin ) const { - const_cast( this )->mSubscribedPlugins.erase( plugin->getId() ); + if ( !mClosing ) + const_cast( this )->mSubscribedPlugins.erase( plugin->getId() ); } void PluginManager::setSplitter( UICodeEditorSplitter* splitter ) {