#ifndef ECODE_AUTOCOMPLETEPLUGIN_HPP #define ECODE_AUTOCOMPLETEPLUGIN_HPP #include "../lsp/lspprotocol.hpp" #include "../plugin.hpp" #include "../pluginmanager.hpp" #include #include #include #include #include #include #include using namespace EE; using namespace EE::System; using namespace EE::UI; namespace ecode { class AutoCompletePlugin : public Plugin { public: class Suggestion { public: LSPCompletionItemKind kind{ LSPCompletionItemKind::Text }; std::string text; std::string detail; std::string sortText; TextRange range; std::string insertText; double score{ 0 }; LSPMarkupContent documentation; void setScore( const double& score ) const { const_cast( this )->score = score; } Suggestion( const std::string& text ) : text( text ), sortText( text ) {} Suggestion( LSPCompletionItemKind kind, std::string&& text, std::string&& detail, std::string&& sortText, const TextRange& range, std::string&& insertText, LSPMarkupContent&& doc ) : kind( kind ), text( std::move( text ) ), detail( std::move( detail ) ), sortText( sortText.empty() ? std::string{ this->text } : std::move( sortText ) ), range( range ), insertText( std::move( insertText ) ), documentation( doc ) {}; bool operator<( const Suggestion& other ) const { return getCmpStr() < other.getCmpStr(); } bool operator==( const Suggestion& other ) const { return text == other.text; } protected: const std::string* getCmpStr() const { return !sortText.empty() ? &sortText : &text; } }; typedef std::vector SymbolsList; static PluginDefinition Definition() { return { "autocomplete", "Auto Complete", "Auto complete shows the completion popup as you type, so you can fill " "in long words by typing only a few characters.", AutoCompletePlugin::New, { 0, 2, 8 }, AutoCompletePlugin::NewSync }; } static Plugin* New( PluginManager* pluginManager ); static Plugin* NewSync( PluginManager* pluginManager ); virtual ~AutoCompletePlugin(); std::string getId() override { return Definition().id; } std::string getTitle() override { return Definition().name; } std::string getDescription() override { return Definition().description; } bool isReady() const override { return true; } void onRegister( UICodeEditor* ) override; void onUnregister( UICodeEditor* ) override; bool onKeyDown( UICodeEditor*, const KeyEvent& ) override; bool onTextInput( UICodeEditor*, const TextInputEvent& ) override; void update( UICodeEditor* ) override; void postDraw( UICodeEditor*, const Vector2f& startScroll, const Float& lineHeight, const TextPosition& cursor ) override; bool onMouseDown( UICodeEditor*, const Vector2i&, const Uint32& ) override; bool onMouseUp( UICodeEditor*, const Vector2i&, const Uint32& ) override; bool onMouseDoubleClick( UICodeEditor*, const Vector2i&, const Uint32& ) override; bool onMouseMove( UICodeEditor*, const Vector2i&, const Uint32& ) override; const Rectf& getBoxPadding() const; void setBoxPadding( const Rectf& boxPadding ); const Int32& getSuggestionsMaxVisible() const; void setSuggestionsMaxVisible( const Uint32& suggestionsMaxVisible ); const Time& getUpdateFreq() const; void setUpdateFreq( const Time& updateFreq ); const std::string& getSymbolPattern() const; void setSymbolPattern( const std::string& symbolPattern ); bool isDirty() const; void setDirty( bool dirty ); bool onCreateContextMenu( UICodeEditor* editor, UIPopUpMenu* menu, const Vector2i& position, const Uint32& flags ) override; protected: std::string mSymbolPattern; Rectf mBoxPadding; Clock mClock; Mutex mLangSymbolsMutex; Mutex mSuggestionsMutex; Mutex mDocMutex; Time mUpdateFreq{ Seconds( 5 ) }; std::unordered_map> mEditors; std::unordered_set mDocs; std::unordered_map mEditorDocs; bool mDirty{ false }; bool mReplacing{ false }; bool mSignatureHelpVisible{ false }; bool mHighlightSuggestions{ true }; struct DocCache { Uint64 changeId{ static_cast( -1 ) }; SymbolsList symbols; }; std::unordered_map mDocCache; std::unordered_map mDocUsesOwnSymbols; std::unordered_map mLangCache; std::vector mSuggestions; Mutex mSuggestionsEditorMutex; Mutex mSignatureHelpEditorMutex; UICodeEditor* mSuggestionsEditor{ nullptr }; UICodeEditor* mSignatureHelpEditor{ nullptr }; Int32 mSuggestionIndex{ 0 }; Int32 mSuggestionsMaxVisible{ 8 }; Int32 mSuggestionsStartIndex{ 0 }; std::unordered_map mCapabilities; Mutex mCapabilitiesMutex; Mutex mDocUsesOwnSymbolsMutex; struct SignatureInformation { String label; LSPMarkupContent documentation; std::vector parameters; }; struct SignatureHelp { std::vector signatures; int activeSignature{ 0 }; int activeParameter{ 0 }; }; SignatureHelp mSignatureHelp; TextPosition mSignatureHelpPosition; Int32 mSignatureHelpSelected{ -1 }; Mutex mHandlesMutex; std::unordered_map> mHandles; std::unordered_map> mDocsUpdating; Mutex mDocsUpdatingMutex; Text mSuggestionDoc; size_t mMaxLabelCharacters{ 100 }; String::HashType mConfigHash{ 0 }; std::unordered_map mKeyBindings; std::unordered_map mShortcuts; std::string mMaxSuggestionDocumentationWidth; Float mRowHeight{ 0 }; Rectf mBoxRect; explicit AutoCompletePlugin( PluginManager* pluginManager, bool sync ); void load( PluginManager* pluginManager ); void resetSuggestions( UICodeEditor* editor ); void updateSuggestions( const std::string& symbol, UICodeEditor* editor ); SymbolsList getDocumentSymbols( TextDocument* ); void updateDocCache( TextDocument* doc ); std::string getPartialSymbol( TextDocument* doc ); void runUpdateSuggestions( const std::string& symbol, const SymbolsList& symbols, UICodeEditor* editor, bool fromDocCache ); void updateLangCache( const std::string& langName ); void pickSuggestion( UICodeEditor* editor ); PluginRequestHandle processResponse( const PluginMessage& msg ); bool tryRequestCapabilities( UICodeEditor* editor ); void requestCodeCompletion( UICodeEditor* editor ); void requestSignatureHelp( UICodeEditor* editor ); PluginRequestHandle processCodeCompletion( const LSPCompletionList& completion ); PluginRequestHandle processSignatureHelp( const LSPSignatureHelp& signatureHelp ); void resetSignatureHelp(); void drawSignatureHelp( UICodeEditor* editor, const Vector2f& startScroll, const Float& lineHeight, bool drawUp ); bool hasCompleteSteps( const Suggestion& suggestion ); void tryStartSnippetNav( const Suggestion& suggestion, UICodeEditor* editor, const TextRanges& prevSels ); Rectf findBestDocumentationPlacement( UICodeEditor* editor, const Suggestion& suggestion, const Rectf& anchorBox, const Rectf& rowRect, bool drawUp, Float lineHeight ); void updateShortcuts(); }; } // namespace ecode #endif // ECODE_AUTOCOMPLETEPLUGIN_HPP