diff --git a/bin/assets/plugins/linters.json b/bin/assets/plugins/linters.json index e6d269b05..0630ade0e 100644 --- a/bin/assets/plugins/linters.json +++ b/bin/assets/plugins/linters.json @@ -65,7 +65,7 @@ "file_patterns": ["%.inl$", "%.cpp$", "%.hpp$", "%.cc$", "%.cxx$", "%.c++$", "%.hh$", "%.hxx$", "%.h++$", "%.objcpp$"], "warning_pattern": "$FILENAME:(%d+):(%d+):%s?([^%s]*)([^\n]*)", "warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 }, - "command": "cppcheck --language=c++ --enable=all --template=gcc --inline-suppr $FILENAME", + "command": "cppcheck --language=c++ --enable=all --template=gcc --inline-suppr --disable=unusedFunction $FILENAME", "url": "https://github.com/danmar/cppcheck" }, { diff --git a/src/tools/ecode/featureshealth.cpp b/src/tools/ecode/featureshealth.cpp index 40ba1e27c..b46c0a5f4 100644 --- a/src/tools/ecode/featureshealth.cpp +++ b/src/tools/ecode/featureshealth.cpp @@ -23,7 +23,7 @@ inline static bool sortKey( const FeaturesHealth::LangHealth& struct1, } std::vector FeaturesHealth::getHealth( PluginManager* pluginManager, - const std::string& lang ) { + const std::string& langFilter ) { std::vector langs; bool ownsLinter = false; bool ownsFormatter = false; @@ -56,7 +56,7 @@ std::vector FeaturesHealth::getHealth( PluginManager if ( !def.isVisible() ) continue; - if ( !lang.empty() && lang != def.getLSPName() ) + if ( !langFilter.empty() && langFilter != def.getLSPName() ) continue; FeaturesHealth::LangHealth lang; @@ -213,7 +213,6 @@ void FeaturesHealth::doHealth( PluginManager* pluginManager, const std::string& auto& hr = healthRes[0]; const std::string notFound = "Not found in $PATH"; - const std::string none = "None"; std::string lspName = hr.lsp.name.empty() ? "\033[33mNone" : "\033[32m" + hr.lsp.name; std::string lspBinary = hr.lsp.found ? "\033[32m" + hr.lsp.path : "\033[31m" + notFound; diff --git a/src/tools/ecode/plugins/linter/linterplugin.cpp b/src/tools/ecode/plugins/linter/linterplugin.cpp index 405eca7e4..b2a596584 100644 --- a/src/tools/ecode/plugins/linter/linterplugin.cpp +++ b/src/tools/ecode/plugins/linter/linterplugin.cpp @@ -54,6 +54,11 @@ LinterPlugin::~LinterPlugin() { } for ( const auto& editor : mEditors ) { + for ( auto& kb : mKeyBindings ) { + editor.first->getKeyBindings().removeCommandKeybind( kb.first ); + if ( editor.first->hasDocument() ) + editor.first->getDocument().removeCommand( kb.first ); + } for ( auto listener : editor.second ) editor.first->removeEventListener( listener ); editor.first->unregisterPlugin( this ); @@ -87,7 +92,8 @@ void LinterPlugin::loadLinterConfig( const std::string& path, bool updateConfigF if ( !updateConfigFile ) return; // Recreate it - j = json::parse( "{\n\"config\":{},\n\"linters\":[]\n}\n", nullptr, true, true ); + j = json::parse( "{\n\"config\":{},\n \"keybindings\":{},\n\"linters\":[]\n}\n", nullptr, + true, true ); } if ( updateConfigFile ) { @@ -151,6 +157,28 @@ void LinterPlugin::loadLinterConfig( const std::string& path, bool updateConfigF } else if ( updateConfigFile ) { config["disable_languages"] = json::array(); } + + if ( config.contains( "go-to-ignore-warnings" ) && + config["go-to-ignore-warnings"].is_boolean() ) { + mGoToIgnoreWarnings = config["go-to-ignore-warnings"].get(); + } else if ( updateConfigFile ) { + config["go-to-ignore-warnings"] = false; + } + } + + if ( mKeyBindings.empty() ) { + mKeyBindings["linter-go-to-next-error"] = "mod+shift+n"; + mKeyBindings["linter-go-to-previous-error"] = "mod+shift+alt+n"; + } + + auto& kb = j["keybindings"]; + auto list = { "linter-go-to-next-error", "linter-go-to-previous-error" }; + for ( const auto& key : list ) { + if ( kb.contains( key ) ) { + if ( !kb[key].empty() ) + mKeyBindings[key] = kb[key]; + } else if ( updateConfigFile ) + kb[key] = mKeyBindings[key]; } if ( updateConfigFile ) { @@ -450,12 +478,32 @@ void LinterPlugin::onRegister( UICodeEditor* editor ) { listeners.push_back( editor->addEventListener( Event::OnTextChanged, [&, editor]( const Event* ) { setDocDirty( editor ); } ) ); + for ( auto& kb : mKeyBindings ) { + if ( !kb.second.empty() ) + editor->getKeyBindings().addKeybindString( kb.second, kb.first ); + } + + if ( editor->hasDocument() ) { + auto& doc = editor->getDocument(); + + doc.setCommand( "linter-go-to-next-error", [this, editor]() { goToNextError( editor ); } ); + + doc.setCommand( "linter-go-to-previous-error", + [this, editor]() { goToPrevError( editor ); } ); + } + mEditors.insert( { editor, listeners } ); mDocs.insert( editor->getDocumentRef().get() ); mEditorDocs[editor] = editor->getDocumentRef().get(); } void LinterPlugin::onUnregister( UICodeEditor* editor ) { + for ( auto& kb : mKeyBindings ) { + editor->getKeyBindings().removeCommandKeybind( kb.first ); + if ( editor->hasDocument() ) + editor->getDocument().removeCommand( kb.first ); + } + if ( mShuttingDown ) return; Lock l( mDocMutex ); @@ -465,8 +513,8 @@ void LinterPlugin::onUnregister( UICodeEditor* editor ) { editor->removeEventListener( listener ); mEditors.erase( editor ); mEditorDocs.erase( editor ); - for ( auto editor : mEditorDocs ) - if ( editor.second == doc ) + for ( auto editorIt : mEditorDocs ) + if ( editorIt.second == doc ) return; mDocs.erase( doc ); mDirtyDoc.erase( doc ); @@ -885,6 +933,112 @@ void LinterPlugin::tryHideHoveringMatch( UICodeEditor* editor ) { } } +void LinterPlugin::goToNextError( UICodeEditor* editor ) { + if ( nullptr == editor || !editor->hasDocument() ) + return; + TextDocument* doc = &editor->getDocument(); + auto pos = doc->getSelection().start(); + + Lock l( mMatchesMutex ); + auto fMatch = mMatches.find( doc ); + if ( fMatch == mMatches.end() ) + return; + const auto& matches = fMatch->second; + if ( matches.empty() ) + return; + + const LinterMatch* matched = nullptr; + for ( const auto& match : matches ) { + if ( match.first > pos.line() ) { + if ( mGoToIgnoreWarnings ) { + for ( const auto& m : match.second ) { + if ( m.type == LinterType::Error ) { + matched = &m; + break; + } + } + } else { + matched = &match.second.front(); + break; + } + } + } + + if ( matched != nullptr ) { + editor->goToLine( matched->range.start() ); + } else { + if ( mGoToIgnoreWarnings ) { + for ( const auto& m : matches ) { + if ( m.first < pos.line() ) { + for ( const auto& lm : m.second ) { + if ( lm.type == LinterType::Error ) { + editor->goToLine( lm.range.start() ); + break; + } + } + } else { + break; + } + } + } else if ( matches.begin()->second.front().range.start().line() != pos.line() ) { + editor->goToLine( matches.begin()->second.front().range.start() ); + } + } +} + +void LinterPlugin::goToPrevError( UICodeEditor* editor ) { + if ( nullptr == editor || !editor->hasDocument() ) + return; + TextDocument* doc = &editor->getDocument(); + auto pos = doc->getSelection().start(); + + Lock l( mMatchesMutex ); + auto fMatch = mMatches.find( doc ); + if ( fMatch == mMatches.end() ) + return; + auto& matches = fMatch->second; + if ( matches.empty() ) + return; + + const LinterMatch* matched = nullptr; + for ( auto match = matches.rbegin(); match != matches.rend(); ++match ) { + if ( match->first < pos.line() ) { + if ( mGoToIgnoreWarnings ) { + for ( const auto& m : match->second ) { + if ( m.type == LinterType::Error ) { + matched = &m; + break; + } + } + } else { + matched = &match->second.front(); + break; + } + } + } + + if ( matched != nullptr ) { + editor->goToLine( matched->range.start() ); + } else { + if ( mGoToIgnoreWarnings ) { + for ( auto m = matches.rbegin(); m != matches.rend(); ++m ) { + if ( m->first > pos.line() ) { + for ( const auto& lm : m->second ) { + if ( lm.type == LinterType::Error ) { + editor->goToLine( lm.range.start() ); + break; + } + } + } else { + break; + } + } + } else if ( matches.rbegin()->second.front().range.start().line() != pos.line() ) { + editor->goToLine( matches.rbegin()->second.front().range.start() ); + } + } +} + bool LinterPlugin::onMouseMove( UICodeEditor* editor, const Vector2i& pos, const Uint32& flags ) { if ( flags != 0 ) { tryHideHoveringMatch( editor ); diff --git a/src/tools/ecode/plugins/linter/linterplugin.hpp b/src/tools/ecode/plugins/linter/linterplugin.hpp index df8c59afa..4c46cd946 100644 --- a/src/tools/ecode/plugins/linter/linterplugin.hpp +++ b/src/tools/ecode/plugins/linter/linterplugin.hpp @@ -119,10 +119,12 @@ class LinterPlugin : public Plugin { std::mutex mWorkMutex; std::condition_variable mWorkerCondition; Int32 mWorkersCount{ 0 }; + std::map mKeyBindings; /* cmd, shortcut */ bool mHoveringMatch{ false }; bool mEnableLSPDiagnostics{ true }; bool mErrorLens{ true }; + bool mGoToIgnoreWarnings{ false }; std::set mLanguagesDisabled; std::set mLSPLanguagesDisabled; String::HashType mConfigHash{ 0 }; @@ -162,6 +164,10 @@ class LinterPlugin : public Plugin { std::map>& matches ); void tryHideHoveringMatch( UICodeEditor* editor ); + + void goToNextError( UICodeEditor* editor ); + + void goToPrevError( UICodeEditor* editor ); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index 771205771..98660607b 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -800,7 +800,7 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std mKeyBindings["lsp-go-to-definition"] = "f2"; mKeyBindings["lsp-symbol-info"] = "f1"; mKeyBindings["lsp-symbol-code-action"] = "alt+return"; - mKeyBindings["lsp-rename-symbol-under-cursor"] = "ctrl+shift+r"; + mKeyBindings["lsp-rename-symbol-under-cursor"] = "mod+shift+r"; } if ( j.contains( "keybindings" ) ) { diff --git a/src/tools/ecode/statusbuildoutputcontroller.cpp b/src/tools/ecode/statusbuildoutputcontroller.cpp index 6573746f4..0cc08a0ce 100644 --- a/src/tools/ecode/statusbuildoutputcontroller.cpp +++ b/src/tools/ecode/statusbuildoutputcontroller.cpp @@ -259,7 +259,7 @@ void StatusBuildOutputController::runBuild( const std::string& buildName, } if ( !mApp->getWindow()->hasFocus() ) - mApp->getWindow()->flash( WindowFlashOperation::Briefly ); + mApp->getWindow()->flash( WindowFlashOperation::UntilFocused ); } ); if ( !res.isValid() ) { @@ -349,7 +349,7 @@ void StatusBuildOutputController::runClean( const std::string& buildName, } if ( !mApp->getWindow()->hasFocus() ) - mApp->getWindow()->flash( WindowFlashOperation::Briefly ); + mApp->getWindow()->flash( WindowFlashOperation::UntilFocused ); } ); if ( !res.isValid() ) {