diff --git a/bin/assets/plugins/lspclient.json b/bin/assets/plugins/lspclient.json index 219376e45..b11a75da4 100644 --- a/bin/assets/plugins/lspclient.json +++ b/bin/assets/plugins/lspclient.json @@ -199,7 +199,7 @@ { "language": "haskell", "name": "haskell-language-server", - "url": "https://github.com/haskell/haskell-language-server --lsp", + "url": "https://github.com/haskell/haskell-language-server", "command": "haskell-language-server-wrapper", "file_patterns": ["%.hs$"], "rootIndicationFileNames": ["%.cabal$", "stack.yaml", "cabal.project", "package.yaml", "hie.yaml"] diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index 6d8e6ae50..9340f65f5 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -347,18 +347,16 @@ class EE_API TextDocument { bool removeCommand( const std::string& command ); - TextRange find( const String& text, TextPosition from = { 0, 0 }, - const bool& caseSensitive = true, const bool& wholeWord = false, - const FindReplaceType& type = FindReplaceType::Normal, + TextRange find( const String& text, TextPosition from = { 0, 0 }, bool caseSensitive = true, + bool wholeWord = false, const FindReplaceType& type = FindReplaceType::Normal, TextRange restrictRange = TextRange() ); - TextRange findLast( const String& text, TextPosition from = { 0, 0 }, - const bool& caseSensitive = true, const bool& wholeWord = false, + TextRange findLast( const String& text, TextPosition from = { 0, 0 }, bool caseSensitive = true, + bool wholeWord = false, const FindReplaceType& type = FindReplaceType::Normal, TextRange restrictRange = TextRange() ); - TextRanges findAll( const String& text, const bool& caseSensitive = true, - const bool& wholeWord = false, + TextRanges findAll( const String& text, bool caseSensitive = true, bool wholeWord = false, const FindReplaceType& type = FindReplaceType::Normal, TextRange restrictRange = TextRange() ); @@ -642,13 +640,13 @@ class EE_API TextDocument { LoadStatus loadFromStream( IOStream& file, std::string path, bool callReset ); - TextRange findText( String text, TextPosition from = { 0, 0 }, const bool& caseSensitive = true, - const bool& wholeWord = false, + TextRange findText( String text, TextPosition from = { 0, 0 }, bool caseSensitive = true, + bool wholeWord = false, const FindReplaceType& type = FindReplaceType::Normal, TextRange restrictRange = TextRange() ); - TextRange findTextLast( String text, TextPosition from = { 0, 0 }, - const bool& caseSensitive = true, const bool& wholeWord = false, + TextRange findTextLast( String text, TextPosition from = { 0, 0 }, bool caseSensitive = true, + bool wholeWord = false, const FindReplaceType& type = FindReplaceType::Normal, TextRange restrictRange = TextRange() ); }; diff --git a/include/eepp/ui/doc/textrange.hpp b/include/eepp/ui/doc/textrange.hpp index fcf408124..67996ae03 100644 --- a/include/eepp/ui/doc/textrange.hpp +++ b/include/eepp/ui/doc/textrange.hpp @@ -113,6 +113,8 @@ class EE_API TextRange { class EE_API TextRanges : public std::vector { public: + bool isSorted() const { return mIsSorted; } + bool isValid() const { for ( const auto& selection : *this ) { if ( !selection.isValid() ) @@ -143,7 +145,12 @@ class EE_API TextRanges : public std::vector { return false; } - void sort() { std::sort( begin(), end() ); } + void sort() { + std::sort( begin(), end() ); + setSorted(); + } + + void setSorted() { mIsSorted = true; } bool merge() { if ( size() <= 1 ) @@ -176,6 +183,9 @@ class EE_API TextRanges : public std::vector { ranges.emplace_back( TextRange::fromString( rangeStr ) ); return ranges; } + + protected: + bool mIsSorted{ false }; }; }}} // namespace EE::UI::Doc diff --git a/include/eepp/ui/tools/uidocfindreplace.hpp b/include/eepp/ui/tools/uidocfindreplace.hpp index 2bc7d961c..5e2713fd2 100644 --- a/include/eepp/ui/tools/uidocfindreplace.hpp +++ b/include/eepp/ui/tools/uidocfindreplace.hpp @@ -69,6 +69,8 @@ class EE_API UIDocFindReplace : public UILinearLayout, public WidgetCommandExecu int replaceAll( TextSearchParams& search, const String& replace ); virtual Uint32 onKeyDown( const KeyEvent& event ); + + void refreshHighlight( UICodeEditor* editor ); }; }}} // namespace EE::UI::Tools diff --git a/src/eepp/system/threadpool.cpp b/src/eepp/system/threadpool.cpp index cb96ab624..87ac0a318 100644 --- a/src/eepp/system/threadpool.cpp +++ b/src/eepp/system/threadpool.cpp @@ -1,3 +1,4 @@ +#include #include namespace EE { namespace System { diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index 231cf0e99..ba8790242 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -2015,8 +2015,8 @@ static std::pair findLastType( const String& str, const String& } } -TextRange TextDocument::findText( String text, TextPosition from, const bool& caseSensitive, - const bool& wholeWord, const FindReplaceType& type, +TextRange TextDocument::findText( String text, TextPosition from, bool caseSensitive, + bool wholeWord, const FindReplaceType& type, TextRange restrictRange ) { if ( text.empty() ) return TextRange(); @@ -2030,7 +2030,11 @@ TextRange TextDocument::findText( String text, TextPosition from, const bool& ca return TextRange(); } - if ( !caseSensitive && type != FindReplaceType::LuaPattern ) + if ( type == FindReplaceType::LuaPattern && caseSensitive == false ) + caseSensitive = + true; // Ignore case insensitive request since this is managed at pattern level + + if ( !caseSensitive ) text.toLower(); for ( Int64 i = from.line(); i <= to.line(); i++ ) { @@ -2064,8 +2068,8 @@ TextRange TextDocument::findText( String text, TextPosition from, const bool& ca return TextRange(); } -TextRange TextDocument::findTextLast( String text, TextPosition from, const bool& caseSensitive, - const bool& wholeWord, const FindReplaceType& type, +TextRange TextDocument::findTextLast( String text, TextPosition from, bool caseSensitive, + bool wholeWord, const FindReplaceType& type, TextRange restrictRange ) { if ( text.empty() ) return TextRange(); @@ -2079,7 +2083,11 @@ TextRange TextDocument::findTextLast( String text, TextPosition from, const bool return TextRange(); } - if ( !caseSensitive && type != FindReplaceType::LuaPattern ) + if ( type == FindReplaceType::LuaPattern && caseSensitive == false ) + caseSensitive = + true; // Ignore case insensitive request since this is managed at pattern level + + if ( !caseSensitive ) text.toLower(); for ( Int64 i = from.line(); i >= to.line(); i-- ) { @@ -2115,8 +2123,8 @@ TextRange TextDocument::findTextLast( String text, TextPosition from, const bool return TextRange(); } -TextRange TextDocument::find( const String& text, TextPosition from, const bool& caseSensitive, - const bool& wholeWord, const FindReplaceType& type, +TextRange TextDocument::find( const String& text, TextPosition from, bool caseSensitive, + bool wholeWord, const FindReplaceType& type, TextRange restrictRange ) { std::vector textLines = text.split( '\n', true, true ); @@ -2193,8 +2201,8 @@ TextRange TextDocument::find( const String& text, TextPosition from, const bool& return TextRange(); } -TextRange TextDocument::findLast( const String& text, TextPosition from, const bool& caseSensitive, - const bool& wholeWord, const FindReplaceType& type, +TextRange TextDocument::findLast( const String& text, TextPosition from, bool caseSensitive, + bool wholeWord, const FindReplaceType& type, TextRange restrictRange ) { std::vector textLines = text.split( '\n', true, true ); @@ -2266,9 +2274,8 @@ TextRange TextDocument::findLast( const String& text, TextPosition from, const b return TextRange(); } -TextRanges TextDocument::findAll( const String& text, const bool& caseSensitive, - const bool& wholeWord, const FindReplaceType& type, - TextRange restrictRange ) { +TextRanges TextDocument::findAll( const String& text, bool caseSensitive, bool wholeWord, + const FindReplaceType& type, TextRange restrictRange ) { TextRanges all; TextRange found; TextPosition from = startOfDoc(); @@ -2283,6 +2290,8 @@ TextRanges TextDocument::findAll( const String& text, const bool& caseSensitive, all.push_back( found ); } } while ( found.isValid() ); + if ( !all.empty() ) + all.setSorted(); return all; } diff --git a/src/eepp/ui/tools/uidocfindreplace.cpp b/src/eepp/ui/tools/uidocfindreplace.cpp index ff6d26c72..2a4bca1e8 100644 --- a/src/eepp/ui/tools/uidocfindreplace.cpp +++ b/src/eepp/ui/tools/uidocfindreplace.cpp @@ -200,22 +200,9 @@ UIDocFindReplace::UIDocFindReplace( UIWidget* parent, const std::shared_ptrsetEscapePastedText( true ); UICodeEditor* editor = getParent()->isType( UI_TYPE_CODEEDITOR ) ? getParent()->asType() : nullptr; - mFindInput->addEventListener( Event::OnTextChanged, [&, editor]( const Event* ) { - mSearchState.text = mFindInput->getText(); - if ( editor ) - editor->setHighlightWord( mSearchState ); - if ( !mSearchState.text.empty() ) { - mDoc->setSelection( { 0, 0 } ); - if ( !findNextText( mSearchState ) ) { - mFindInput->addClass( "error" ); - } else { - mFindInput->removeClass( "error" ); - } - } else { - mFindInput->removeClass( "error" ); - mDoc->setSelection( mDoc->getSelection().start() ); - } - } ); + + mFindInput->addEventListener( Event::OnTextChanged, + [&, editor]( const Event* ) { refreshHighlight( editor ); } ); mFindInput->addEventListener( Event::OnTextPasted, [&]( const Event* ) { if ( mFindInput->getUISceneNode()->getWindow()->getClipboard()->getText().find( '\n' ) != String::InvalidPos ) { @@ -235,14 +222,22 @@ UIDocFindReplace::UIDocFindReplace( UIWidget* parent, const std::shared_ptrreplaceSelection( mReplaceInput->getText() ); } ); - setCommand( "change-case", - [&] { mCaseSensitive->setSelected( !mCaseSensitive->isSelected() ); } ); - setCommand( "change-whole-word", - [&] { mWholeWord->setSelected( !mWholeWord->isSelected() ); } ); - setCommand( "change-escape-sequence", - [&] { mEscapeSequences->setSelected( !mEscapeSequences->isSelected() ); } ); - setCommand( "toggle-lua-pattern", - [&] { mLuaPattern->setSelected( !mLuaPattern->isSelected() ); } ); + setCommand( "change-case", [this, editor] { + mCaseSensitive->setSelected( !mCaseSensitive->isSelected() ); + refreshHighlight( editor ); + } ); + setCommand( "change-whole-word", [this, editor] { + mWholeWord->setSelected( !mWholeWord->isSelected() ); + refreshHighlight( editor ); + } ); + setCommand( "change-escape-sequence", [this, editor] { + mEscapeSequences->setSelected( !mEscapeSequences->isSelected() ); + refreshHighlight( editor ); + } ); + setCommand( "toggle-lua-pattern", [this, editor] { + mLuaPattern->setSelected( !mLuaPattern->isSelected() ); + refreshHighlight( editor ); + } ); mCaseSensitive->setTooltipText( mCaseSensitive->getTooltipText() + " (" + getKeyBindings().getCommandKeybindString( "change-case" ) + @@ -316,6 +311,11 @@ UIDocFindReplace::UIDocFindReplace( UIWidget* parent, const std::shared_ptr::New( &mSearchState.type, mLuaPattern, luaPatternConverter ); + auto valueChangeCb = [this, editor]( const auto& ) { refreshHighlight( editor ); }; + mPatternBind->onValueChangeCb = valueChangeCb; + for ( const auto& db : mDataBinds ) + db->onValueChangeCb = valueChangeCb; + setVisible( false ); runOnMainThread( [&] { mReady = true; } ); @@ -518,4 +518,21 @@ bool UIDocFindReplace::findAndReplace( TextSearchParams& search, const String& r } } +void UIDocFindReplace::refreshHighlight( UICodeEditor* editor ) { + mSearchState.text = mFindInput->getText(); + if ( editor ) + editor->setHighlightWord( mSearchState ); + if ( !mSearchState.text.empty() ) { + mDoc->setSelection( { 0, 0 } ); + if ( !findNextText( mSearchState ) ) { + mFindInput->addClass( "error" ); + } else { + mFindInput->removeClass( "error" ); + } + } else { + mFindInput->removeClass( "error" ); + mDoc->setSelection( mDoc->getSelection().start() ); + } +}; + }}} // namespace EE::UI::Tools diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 6698597d5..f492e5c2a 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -3338,6 +3338,9 @@ void UICodeEditor::drawMinimap( const Vector2f& start, !range.inSameLine() ) continue; + if ( ranges.isSorted() && range.end().line() > endidx ) + break; + Rectf selRect; selRect.Top = rect.Top + ( range.start().line() - minimapStartLine ) * lineSpacing; selRect.Bottom = selRect.Top + charHeight; diff --git a/src/tools/ecode/docsearchcontroller.cpp b/src/tools/ecode/docsearchcontroller.cpp index 7b4442ada..d0da6120c 100644 --- a/src/tools/ecode/docsearchcontroller.cpp +++ b/src/tools/ecode/docsearchcontroller.cpp @@ -6,6 +6,25 @@ namespace ecode { DocSearchController::DocSearchController( UICodeEditorSplitter* editorSplitter, App* app ) : mEditorSplitter( editorSplitter ), mApp( app ) {} +void DocSearchController::refreshHighlight() { + if ( mSearchState.editor && mEditorSplitter->editorExists( mSearchState.editor ) ) { + mSearchState.text = mFindInput->getText(); + mSearchState.editor->setHighlightWord( mSearchState.toTextSearchParams() ); + if ( !mSearchState.text.empty() ) { + mSearchState.editor->getDocument().setSelection( { 0, 0 } ); + if ( !findNextText( mSearchState ) ) { + mFindInput->addClass( "error" ); + } else { + mFindInput->removeClass( "error" ); + } + } else { + mFindInput->removeClass( "error" ); + mSearchState.editor->getDocument().setSelection( + mSearchState.editor->getDocument().getSelection().start() ); + } + } +} + void DocSearchController::initSearchBar( UISearchBar* searchBar, const SearchBarConfig& searchBarConfig, std::unordered_map keybindings ) { @@ -62,39 +81,27 @@ void DocSearchController::initSearchBar( mCaseSensitiveChk->addEventListener( Event::OnValueChange, [&]( const Event* ) { mSearchState.caseSensitive = mCaseSensitiveChk->isChecked(); + refreshHighlight(); } ); mEscapeSequenceChk->addEventListener( Event::OnValueChange, [&]( const Event* ) { mSearchState.escapeSequences = mEscapeSequenceChk->isChecked(); + refreshHighlight(); } ); mWholeWordChk->addEventListener( Event::OnValueChange, [&]( const Event* ) { mSearchState.wholeWord = mWholeWordChk->isChecked(); + refreshHighlight(); } ); mLuaPatternChk->addEventListener( Event::OnValueChange, [&]( const Event* ) { mSearchState.type = mLuaPatternChk->isChecked() ? TextDocument::FindReplaceType::LuaPattern : TextDocument::FindReplaceType::Normal; + refreshHighlight(); } ); - mFindInput->addEventListener( Event::OnTextChanged, [&]( const Event* ) { - if ( mSearchState.editor && mEditorSplitter->editorExists( mSearchState.editor ) ) { - mSearchState.text = mFindInput->getText(); - mSearchState.editor->setHighlightWord( mSearchState.toTextSearchParams() ); - if ( !mSearchState.text.empty() ) { - mSearchState.editor->getDocument().setSelection( { 0, 0 } ); - if ( !findNextText( mSearchState ) ) { - mFindInput->addClass( "error" ); - } else { - mFindInput->removeClass( "error" ); - } - } else { - mFindInput->removeClass( "error" ); - mSearchState.editor->getDocument().setSelection( - mSearchState.editor->getDocument().getSelection().start() ); - } - } - } ); + mFindInput->addEventListener( Event::OnTextChanged, + [&]( const Event* ) { refreshHighlight(); } ); mFindInput->addEventListener( Event::OnTextPasted, [&]( const Event* ) { if ( mFindInput->getUISceneNode()->getWindow()->getClipboard()->getText().find( '\n' ) != String::InvalidPos ) { diff --git a/src/tools/ecode/docsearchcontroller.hpp b/src/tools/ecode/docsearchcontroller.hpp index bca610104..5ac5e629a 100644 --- a/src/tools/ecode/docsearchcontroller.hpp +++ b/src/tools/ecode/docsearchcontroller.hpp @@ -72,6 +72,7 @@ class DocSearchController { void selectAll( SearchState& search ); + void refreshHighlight(); protected: UICodeEditorSplitter* mEditorSplitter{ nullptr }; UITextInput* mFindInput{ nullptr }; diff --git a/src/tools/ecode/featureshealth.cpp b/src/tools/ecode/featureshealth.cpp index bde58cd3f..c13171668 100644 --- a/src/tools/ecode/featureshealth.cpp +++ b/src/tools/ecode/featureshealth.cpp @@ -86,6 +86,7 @@ std::vector FeaturesHealth::getHealth( PluginManager lsp->getClientManager().getLSPForLang( def.getLSPName(), def.getFiles() ); if ( !found.command.empty() ) { lang.lsp.name = found.name; + lang.lsp.url = found.url; lang.lsp.path = Sys::which( String::split( found.command, ' ' )[0] ); lang.lsp.found = !lang.lsp.path.empty(); } @@ -128,8 +129,12 @@ std::string FeaturesHealth::generateHealthStatus( PluginManager* pluginManager, } for ( const auto& ht : status ) { - table.add_row( { ht.lang, "✓", ht.lsp.name.empty() ? "None" : ht.lsp.name, - ht.linter.name.empty() ? "None" : ht.linter.name, + std::string lspName = ht.lsp.name.empty() ? "None" : ht.lsp.name; + if ( OutputFormat::Markdown == format && !ht.lsp.name.empty() && !ht.lsp.url.empty() ) { + lspName = "[" + ht.lsp.name + "](" + ht.lsp.url + ")"; + } + + table.add_row( { ht.lang, "✓", lspName, ht.linter.name.empty() ? "None" : ht.linter.name, ht.formatter.name.empty() ? "None" : ht.formatter.name } ); auto& row = table[table.size() - 1]; diff --git a/src/tools/ecode/featureshealth.hpp b/src/tools/ecode/featureshealth.hpp index b7f5b418f..eacd6ccd9 100644 --- a/src/tools/ecode/featureshealth.hpp +++ b/src/tools/ecode/featureshealth.hpp @@ -25,6 +25,7 @@ class FeaturesHealth { struct FeatureStatus { std::string name; std::string path; + std::string url; bool found{ false }; }; struct LangHealth { diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index 548005605..56fd399a6 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -802,9 +802,9 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std lsp.language = obj["language"]; lsp.command = parseCommand( obj["command"] ); lsp.name = obj["name"]; + lsp.url = obj.value( "url", "" ); } - lsp.url = obj.value( "url", "" ); lsp.commandParameters = obj.value( "command_parameters", lsp.commandParameters ); if ( obj.contains( "initializationOptions" ) ) lsp.initializationOptions = obj["initializationOptions"];