mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
ecode: Added codicon to support completion symbols icons.
Improved auto-complete plugin and several fixes. Implementing signature help.
This commit is contained in:
BIN
bin/assets/fonts/codicon.ttf
Normal file
BIN
bin/assets/fonts/codicon.ttf
Normal file
Binary file not shown.
Binary file not shown.
@@ -81,6 +81,7 @@ class EE_API Event {
|
||||
OnSelectionChanged,
|
||||
OnNodeDropped,
|
||||
OnDocumentSave,
|
||||
OnDocumentUndoRedo,
|
||||
OnModelEvent,
|
||||
OnResourceChange,
|
||||
OnActiveWidgetChange,
|
||||
|
||||
@@ -140,6 +140,10 @@ class EE_API TextDocument {
|
||||
|
||||
String getSelectedText() const;
|
||||
|
||||
String::StringBaseType getPrevChar() const;
|
||||
|
||||
String::StringBaseType getCurrentChar() const;
|
||||
|
||||
String::StringBaseType getChar( const TextPosition& position ) const;
|
||||
|
||||
TextPosition insert( const TextPosition& position, const String& text );
|
||||
|
||||
@@ -24,6 +24,7 @@ cp -r ../../../bin/assets/colorschemes ecode.app/assets/
|
||||
cp -r ../../../bin/assets/fonts/DejaVuSansMono.ttf ecode.app/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/DejaVuSansMonoNerdFontComplete.ttf ecode.app/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/nonicons.ttf ecode.app/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/codicon.ttf ecode.app/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/NotoSans-Regular.ttf ecode.app/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/remixicon.ttf ecode.app/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/NotoEmoji-Regular.ttf ecode.app/assets/fonts/
|
||||
|
||||
@@ -20,6 +20,7 @@ mkdir -p ecode.app/Contents/MacOS/assets/fonts
|
||||
cp -r ../../../bin/assets/fonts/DejaVuSansMono.ttf ecode.app/Contents/MacOS/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/DejaVuSansMonoNerdFontComplete.ttf ecode.app/Contents/MacOS/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/nonicons.ttf ecode.app/Contents/MacOS/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/codicon.ttf ecode.app/Contents/MacOS/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/NotoSans-Regular.ttf ecode.app/Contents/MacOS/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/remixicon.ttf ecode.app/Contents/MacOS/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/NotoEmoji-Regular.ttf ecode.app/Contents/MacOS/assets/fonts/
|
||||
|
||||
@@ -44,6 +44,7 @@ cp -r ../../../bin/assets/colorschemes ecode/assets/
|
||||
cp -r ../../../bin/assets/fonts/DejaVuSansMono.ttf ecode/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/DejaVuSansMonoNerdFontComplete.ttf ecode/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/nonicons.ttf ecode/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/codicon.ttf ecode/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/NotoSans-Regular.ttf ecode/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/remixicon.ttf ecode/assets/fonts/
|
||||
cp -r ../../../bin/assets/fonts/NotoEmoji-Regular.ttf ecode/assets/fonts/
|
||||
|
||||
@@ -668,6 +668,14 @@ String TextDocument::getSelectedText() const {
|
||||
return getText( getSelection() );
|
||||
}
|
||||
|
||||
String::StringBaseType TextDocument::getPrevChar() const {
|
||||
return getChar( positionOffset( getSelection().start(), -1 ) );
|
||||
}
|
||||
|
||||
String::StringBaseType TextDocument::getCurrentChar() const {
|
||||
return getChar( getSelection().start() );
|
||||
}
|
||||
|
||||
String::StringBaseType TextDocument::getChar( const TextPosition& position ) const {
|
||||
auto pos = sanitizePosition( position );
|
||||
return mLines[pos.line()][pos.column()];
|
||||
|
||||
@@ -1482,6 +1482,8 @@ void UICodeEditor::onDocumentLineChanged( const Int64& lineNumber ) {
|
||||
|
||||
void UICodeEditor::onDocumentUndoRedo( const TextDocument::UndoRedo& ) {
|
||||
onDocumentSelectionChange( {} );
|
||||
DocEvent event( this, mDoc.get(), Event::OnDocumentUndoRedo );
|
||||
sendEvent( &event );
|
||||
}
|
||||
|
||||
void UICodeEditor::onDocumentSaved( TextDocument* doc ) {
|
||||
|
||||
@@ -3502,8 +3502,9 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe
|
||||
|
||||
FontTrueType* iconFont = loadFont( "icon", "fonts/remixicon.ttf" );
|
||||
FontTrueType* mimeIconFont = loadFont( "nonicons", "fonts/nonicons.ttf" );
|
||||
FontTrueType* codIconFont = loadFont( "codicon", "fonts/codicon.ttf" );
|
||||
|
||||
if ( !mFont || !mFontMono || !iconFont || !mimeIconFont ) {
|
||||
if ( !mFont || !mFontMono || !iconFont || !mimeIconFont || !codIconFont ) {
|
||||
printf( "Font not found!" );
|
||||
Log::error( "Font not found!" );
|
||||
return;
|
||||
@@ -3872,6 +3873,27 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe
|
||||
iconTheme->add( UIGlyphIcon::New( icon.first, mimeIconFont, icon.second ) );
|
||||
}
|
||||
|
||||
if ( codIconFont && codIconFont->loaded() ) {
|
||||
std::unordered_map<std::string, Uint32> codIcons = {
|
||||
{ "symbol-text", 0xea93 }, { "symbol-method", 0xea8c },
|
||||
{ "symbol-function", 0xea8c }, { "symbol-constructor", 0xea8c },
|
||||
{ "symbol-field", 0xeb5f }, { "symbol-variable", 0xea88 },
|
||||
{ "symbol-class", 0xeb5b }, { "symbol-interface", 0xeb61 },
|
||||
{ "symbol-module", 0xea8b }, { "symbol-property", 0xeb65 },
|
||||
{ "symbol-unit", 0xea96 }, { "symbol-value", 0xea95 },
|
||||
{ "symbol-enum", 0xea95 }, { "symbol-keyword", 0xeb62 },
|
||||
{ "symbol-snippet", 0xeb66 }, { "symbol-color", 0xeb5c },
|
||||
{ "symbol-file", 0xeb60 }, { "symbol-reference", 0xea94 },
|
||||
{ "symbol-folder", 0xea83 }, { "symbol-enum-member", 0xeb5e },
|
||||
{ "symbol-constant", 0xeb5d }, { "symbol-struct", 0xea91 },
|
||||
{ "symbol-event", 0xea86 }, { "symbol-operator", 0xeb64 },
|
||||
{ "symbol-type-parameter", 0xea92 },
|
||||
};
|
||||
|
||||
for ( const auto& icon : codIcons )
|
||||
iconTheme->add( UIGlyphIcon::New( icon.first, codIconFont, icon.second ) );
|
||||
}
|
||||
|
||||
mUISceneNode->getUIIconThemeManager()->setCurrentTheme( iconTheme );
|
||||
|
||||
UIWidgetCreator::registerWidget( "searchbar", UISearchBar::New );
|
||||
|
||||
@@ -18,38 +18,54 @@ namespace ecode {
|
||||
#define AUTO_COMPLETE_THREADED 0
|
||||
#endif
|
||||
|
||||
static std::vector<std::string>
|
||||
static json getURIAndPositionJSON( UICodeEditor* 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() } };
|
||||
return data;
|
||||
}
|
||||
|
||||
static AutoCompletePlugin::SymbolsList
|
||||
fuzzyMatchSymbols( const std::vector<const AutoCompletePlugin::SymbolsList*>& symbolsVec,
|
||||
const std::string& match, const size_t& max ) {
|
||||
std::multimap<int, std::string, std::greater<int>> matchesMap;
|
||||
std::vector<std::string> matches;
|
||||
AutoCompletePlugin::SymbolsList matches;
|
||||
matches.reserve( max );
|
||||
int score;
|
||||
bool firstHasSortText = !symbolsVec[0]->empty()
|
||||
? symbolsVec[0]->at( 0 ).sortText != symbolsVec[0]->at( 0 ).text
|
||||
: false;
|
||||
for ( const auto& symbols : symbolsVec ) {
|
||||
for ( const auto& symbol : *symbols ) {
|
||||
if ( ( score = String::fuzzyMatch( symbol, match ) ) > 0 ) {
|
||||
matchesMap.insert( { score, symbol } );
|
||||
if ( ( score = String::fuzzyMatch( symbol.text, match ) ) > 0 || firstHasSortText ) {
|
||||
if ( std::find( matches.begin(), matches.end(), symbol ) == matches.end() ) {
|
||||
symbol.setScore( score );
|
||||
matches.push_back( symbol );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( firstHasSortText )
|
||||
break;
|
||||
}
|
||||
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 );
|
||||
}
|
||||
if ( firstHasSortText ) {
|
||||
std::sort( matches.begin(), matches.end() );
|
||||
} else {
|
||||
std::sort( matches.begin(), matches.end(),
|
||||
[]( const auto& left, const auto& right ) { return left.score > right.score; } );
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
UICodeEditorPlugin* AutoCompletePlugin::New( const PluginManager* pluginManager ) {
|
||||
UICodeEditorPlugin* AutoCompletePlugin::New( PluginManager* pluginManager ) {
|
||||
return eeNew( AutoCompletePlugin, ( pluginManager ) );
|
||||
}
|
||||
|
||||
AutoCompletePlugin::AutoCompletePlugin( const PluginManager* pluginManager ) :
|
||||
AutoCompletePlugin::AutoCompletePlugin( PluginManager* pluginManager ) :
|
||||
mManager( pluginManager ),
|
||||
mSymbolPattern( "[%a_ñàáâãäåèéêëìíîïòóôõöùúûüýÿÑÀÁÂÃÄÅÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝ][%w_"
|
||||
"ñàáâãäåèéêëìíîïòóôõöùúûüýÿÑÀÁÂÃÄÅÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝ]*" ),
|
||||
mBoxPadding( PixelDensity::dpToPx( Rectf( 4, 4, 4, 4 ) ) ),
|
||||
mBoxPadding( PixelDensity::dpToPx( Rectf( 4, 4, 12, 4 ) ) ),
|
||||
mPool( pluginManager->getThreadPool() ) {
|
||||
mManager->subscribeMessages( this, [&]( const PluginMessage& msg ) -> PluginRequestHandle {
|
||||
return processResponse( msg );
|
||||
@@ -105,6 +121,12 @@ void AutoCompletePlugin::onRegister( UICodeEditor* editor ) {
|
||||
resetSuggestions( editor );
|
||||
} ) );
|
||||
|
||||
listeners.push_back( editor->addEventListener(
|
||||
Event::OnFocusLoss, [&]( const Event* ) { resetSignatureHelp(); } ) );
|
||||
|
||||
listeners.push_back( editor->addEventListener(
|
||||
Event::OnDocumentUndoRedo, [&]( const Event* ) { resetSignatureHelp(); } ) );
|
||||
|
||||
listeners.push_back(
|
||||
editor->addEventListener( Event::OnDocumentSyntaxDefinitionChange, [&]( const Event* ev ) {
|
||||
const DocSyntaxDefEvent* event = static_cast<const DocSyntaxDefEvent*>( ev );
|
||||
@@ -134,6 +156,8 @@ void AutoCompletePlugin::onUnregister( UICodeEditor* editor ) {
|
||||
return;
|
||||
if ( mSuggestionsEditor == editor )
|
||||
resetSuggestions( editor );
|
||||
if ( mSignatureHelpEditor == editor )
|
||||
resetSignatureHelp();
|
||||
Lock l( mDocMutex );
|
||||
TextDocument* doc = mEditorDocs[editor];
|
||||
auto cbs = mEditors[editor];
|
||||
@@ -150,6 +174,28 @@ void AutoCompletePlugin::onUnregister( UICodeEditor* editor ) {
|
||||
}
|
||||
|
||||
bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event ) {
|
||||
bool ret = false;
|
||||
if ( mSignatureHelpVisible ) {
|
||||
if ( event.getKeyCode() == KEY_ESCAPE ) {
|
||||
resetSignatureHelp();
|
||||
editor->invalidateDraw();
|
||||
ret = true;
|
||||
} else if ( event.getKeyCode() == EE::Window::KEY_BACKSPACE ||
|
||||
event.getKeyCode() == EE::Window::KEY_DELETE ) {
|
||||
auto lang = editor->getDocumentRef()->getSyntaxDefinition().getLSPName();
|
||||
auto cap = mCapabilities.find( lang );
|
||||
if ( cap != mCapabilities.end() ) {
|
||||
auto curChar = event.getKeyCode() == EE::Window::KEY_BACKSPACE
|
||||
? editor->getDocumentRef()->getPrevChar()
|
||||
: editor->getDocumentRef()->getCurrentChar();
|
||||
const auto& signatureTrigger = cap->second.signatureHelpProvider.triggerCharacters;
|
||||
if ( std::find( signatureTrigger.begin(), signatureTrigger.end(), curChar ) !=
|
||||
signatureTrigger.end() ) {
|
||||
resetSignatureHelp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !mSuggestions.empty() ) {
|
||||
if ( event.getKeyCode() == KEY_DOWN ) {
|
||||
if ( mSuggestionIndex + 1 < (int)mSuggestions.size() ) {
|
||||
@@ -180,6 +226,7 @@ bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event
|
||||
return true;
|
||||
} else if ( event.getKeyCode() == KEY_ESCAPE ) {
|
||||
resetSuggestions( editor );
|
||||
resetSignatureHelp();
|
||||
editor->invalidateDraw();
|
||||
return true;
|
||||
} else if ( event.getKeyCode() == KEY_HOME ) {
|
||||
@@ -224,7 +271,25 @@ bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event
|
||||
updateSuggestions( partialSymbol, editor );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AutoCompletePlugin::requestSignatureHelp( UICodeEditor* editor ) {
|
||||
mSignatureHelpEditor = editor;
|
||||
auto doc = editor->getDocumentRef();
|
||||
mSignatureHelpPosition = editor->getDocumentRef()->getSelection().start();
|
||||
|
||||
mPool->run( [&, editor]() {
|
||||
json data = getURIAndPositionJSON( editor );
|
||||
mManager->sendRequest( this, PluginMessageType::SignatureHelp, PluginMessageFormat::JSON,
|
||||
&data );
|
||||
} );
|
||||
}
|
||||
|
||||
void AutoCompletePlugin::requestCodeCompletion( UICodeEditor* editor ) {
|
||||
json data = getURIAndPositionJSON( editor );
|
||||
mManager->sendRequest( this, PluginMessageType::CodeCompletion, PluginMessageFormat::JSON,
|
||||
&data );
|
||||
}
|
||||
|
||||
bool AutoCompletePlugin::onTextInput( UICodeEditor* editor, const TextInputEvent& event ) {
|
||||
@@ -233,6 +298,20 @@ bool AutoCompletePlugin::onTextInput( UICodeEditor* editor, const TextInputEvent
|
||||
auto lang = editor->getDocumentRef()->getSyntaxDefinition().getLSPName();
|
||||
auto cap = mCapabilities.find( lang );
|
||||
if ( cap != mCapabilities.end() ) {
|
||||
const auto& signatureTrigger = cap->second.signatureHelpProvider.triggerCharacters;
|
||||
if ( std::find( signatureTrigger.begin(), signatureTrigger.end(), event.getChar() ) !=
|
||||
signatureTrigger.end() ) {
|
||||
requestSignatureHelp( editor );
|
||||
}
|
||||
|
||||
if ( mSignatureHelpVisible ) {
|
||||
auto doc = editor->getDocumentRef();
|
||||
auto curPos = doc->getSelection().start();
|
||||
if ( curPos.line() != mSignatureHelpPosition.line() ||
|
||||
curPos < doc->startOfWord( doc->positionOffset( mSignatureHelpPosition, 1 ) ) )
|
||||
resetSignatureHelp();
|
||||
}
|
||||
|
||||
const auto& triggerCharacters = cap->second.completionProvider.triggerCharacters;
|
||||
if ( partialSymbol.size() >= 1 ||
|
||||
std::find( triggerCharacters.begin(), triggerCharacters.end(), event.getChar() ) !=
|
||||
@@ -294,48 +373,74 @@ void AutoCompletePlugin::pickSuggestion( UICodeEditor* editor ) {
|
||||
std::string symbol( getPartialSymbol( editor->getDocumentRef().get() ) );
|
||||
if ( !symbol.empty() )
|
||||
editor->getDocument().execute( "delete-to-previous-word" );
|
||||
editor->getDocument().textInput( mSuggestions[mSuggestionIndex] );
|
||||
editor->getDocument().textInput( mSuggestions[mSuggestionIndex].text );
|
||||
mReplacing = false;
|
||||
resetSuggestions( editor );
|
||||
}
|
||||
|
||||
PluginRequestHandle AutoCompletePlugin::processResponse( const PluginMessage& msg ) {
|
||||
if ( msg.isResponse() && msg.type == PluginMessageType::CodeCompletion ) {
|
||||
auto completion = msg.asCodeCompletion();
|
||||
SymbolsList suggestions;
|
||||
for ( const auto& item : completion ) {
|
||||
if ( !item.textEdit.text.empty() )
|
||||
suggestions.push_back( item.textEdit.text );
|
||||
else if ( !item.insertText.empty() )
|
||||
suggestions.push_back( item.insertText );
|
||||
else
|
||||
suggestions.push_back( item.filterText );
|
||||
}
|
||||
if ( suggestions.empty() || !mSuggestionsEditor )
|
||||
return {};
|
||||
std::string symbol( getPartialSymbol( mSuggestionsEditor->getDocumentRef().get() ) );
|
||||
const std::string& lang =
|
||||
mSuggestionsEditor->getDocument().getSyntaxDefinition().getLanguageName();
|
||||
PluginRequestHandle
|
||||
AutoCompletePlugin::processCodeCompletion( const std::vector<LSPCompletionItem>& completion ) {
|
||||
SymbolsList suggestions;
|
||||
for ( const auto& item : completion ) {
|
||||
if ( !item.textEdit.text.empty() )
|
||||
suggestions.push_back( { item.kind, item.textEdit.text, item.detail, item.sortText,
|
||||
item.textEdit.range } );
|
||||
else if ( !item.insertText.empty() )
|
||||
suggestions.push_back( { item.kind, item.insertText, item.detail, item.sortText } );
|
||||
else
|
||||
suggestions.push_back( { item.kind, item.filterText, item.detail, item.sortText } );
|
||||
}
|
||||
if ( suggestions.empty() || !mSuggestionsEditor )
|
||||
return {};
|
||||
UICodeEditor* editor = nullptr;
|
||||
{
|
||||
Lock l( mSuggestionsEditorMutex );
|
||||
editor = mSuggestionsEditor;
|
||||
}
|
||||
if ( !editor )
|
||||
return {};
|
||||
std::string symbol( getPartialSymbol( editor->getDocumentRef().get() ) );
|
||||
const std::string& lang = editor->getDocument().getSyntaxDefinition().getLanguageName();
|
||||
bool hasLangSuggestions = false;
|
||||
{
|
||||
Lock l2( mLangSymbolsMutex );
|
||||
auto langSuggestions = mLangCache.find( lang );
|
||||
std::vector<std::string> fuzzySuggestions;
|
||||
if ( symbol.empty() ) {
|
||||
Lock l( mSuggestionsMutex );
|
||||
mSuggestions = suggestions;
|
||||
} else {
|
||||
if ( langSuggestions == mLangCache.end() ) {
|
||||
fuzzySuggestions = fuzzyMatchSymbols( { &suggestions }, symbol,
|
||||
eemax<size_t>( 100UL, suggestions.size() ) );
|
||||
} else {
|
||||
auto& symbols = langSuggestions->second;
|
||||
fuzzySuggestions = fuzzyMatchSymbols( { &suggestions, &symbols }, symbol,
|
||||
eemax<size_t>( 100UL, suggestions.size() ) );
|
||||
}
|
||||
Lock l( mSuggestionsMutex );
|
||||
mSuggestions = fuzzySuggestions;
|
||||
hasLangSuggestions = langSuggestions == mLangCache.end();
|
||||
}
|
||||
if ( symbol.empty() || hasLangSuggestions ) {
|
||||
Lock l( mSuggestionsMutex );
|
||||
mSuggestions = suggestions;
|
||||
} else {
|
||||
SymbolsList fuzzySuggestions;
|
||||
{
|
||||
Lock l2( mLangSymbolsMutex );
|
||||
auto& symbols = mLangCache[lang];
|
||||
fuzzySuggestions = fuzzyMatchSymbols( { &suggestions, &symbols }, symbol,
|
||||
eemax<size_t>( 100UL, suggestions.size() ) );
|
||||
}
|
||||
Lock l( mSuggestionsMutex );
|
||||
mSuggestions = fuzzySuggestions;
|
||||
}
|
||||
|
||||
mSuggestionsEditor->runOnMainThread( [this] { mSuggestionsEditor->invalidateDraw(); } );
|
||||
editor->runOnMainThread( [editor] { editor->invalidateDraw(); } );
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
PluginRequestHandle
|
||||
AutoCompletePlugin::processSignatureHelp( const LSPSignatureHelp& signatureHelp ) {
|
||||
mSignatureHelpVisible = true;
|
||||
mSignatureHelp = signatureHelp;
|
||||
if ( mSignatureHelp.signatures.empty() )
|
||||
resetSignatureHelp();
|
||||
return {};
|
||||
}
|
||||
|
||||
PluginRequestHandle AutoCompletePlugin::processResponse( const PluginMessage& msg ) {
|
||||
if ( msg.isResponse() && msg.type == PluginMessageType::CodeCompletion ) {
|
||||
return processCodeCompletion( msg.asCodeCompletion() );
|
||||
} else if ( msg.isResponse() && msg.type == PluginMessageType::SignatureHelp ) {
|
||||
return processSignatureHelp( msg.asSignatureHelp() );
|
||||
} else if ( msg.isBroadcast() && msg.type == PluginMessageType::LanguageServerCapabilities ) {
|
||||
if ( msg.asLanguageServerCapabilities().ready ) {
|
||||
LSPServerCapabilities cap = msg.asLanguageServerCapabilities();
|
||||
@@ -383,25 +488,52 @@ void AutoCompletePlugin::update( UICodeEditor* ) {
|
||||
|
||||
void AutoCompletePlugin::postDraw( UICodeEditor* editor, const Vector2f& startScroll,
|
||||
const Float& lineHeight, const TextPosition& cursor ) {
|
||||
std::vector<std::string> suggestions;
|
||||
bool drawsSuggestions =
|
||||
!( mSuggestions.empty() || !mSuggestionsEditor || mSuggestionsEditor != editor );
|
||||
bool drawsSignature = mSignatureHelpVisible && mSignatureHelpEditor == editor &&
|
||||
!mSignatureHelp.signatures.empty() && mSignatureHelpPosition.isValid();
|
||||
if ( !drawsSuggestions && !drawsSignature )
|
||||
return;
|
||||
|
||||
TextPosition start =
|
||||
editor->getDocument().startOfWord( editor->getDocument().startOfWord( cursor ) );
|
||||
Primitives primitives;
|
||||
const SyntaxColorScheme& scheme = editor->getColorScheme();
|
||||
const auto& normalStyle = scheme.getEditorSyntaxStyle( "suggestion" );
|
||||
const auto& selectedStyle = scheme.getEditorSyntaxStyle( "suggestion_selected" );
|
||||
|
||||
if ( drawsSignature ) {
|
||||
auto cursig = mSignatureHelp.signatures[mSignatureHelp.activeSignature];
|
||||
Vector2f pos( startScroll.x + editor->getXOffsetCol( mSignatureHelpPosition ),
|
||||
startScroll.y + mSignatureHelpPosition.line() * lineHeight - lineHeight -
|
||||
mBoxPadding.Top - mBoxPadding.Bottom );
|
||||
primitives.setColor( Color( selectedStyle.background ).blendAlpha( editor->getAlpha() ) );
|
||||
primitives.drawRoundedRectangle(
|
||||
Rectf( pos, Sizef( editor->getTextWidth( cursig.label ) + mBoxPadding.Left +
|
||||
mBoxPadding.Right,
|
||||
mRowHeight ) ),
|
||||
0.f, Vector2f::One, 6 );
|
||||
Text text( "", editor->getFont(), editor->getFontSize() );
|
||||
text.setFillColor( normalStyle.color );
|
||||
text.setStyle( normalStyle.style );
|
||||
text.setString( cursig.label );
|
||||
text.draw( pos.x + mBoxPadding.Left, pos.y + mBoxPadding.Top );
|
||||
}
|
||||
|
||||
if ( !drawsSuggestions )
|
||||
return;
|
||||
|
||||
SymbolsList suggestions;
|
||||
{
|
||||
Lock l( mSuggestionsMutex );
|
||||
if ( mSuggestions.empty() || !mSuggestionsEditor || mSuggestionsEditor != editor )
|
||||
return;
|
||||
suggestions = mSuggestions;
|
||||
}
|
||||
|
||||
Primitives primitives;
|
||||
TextPosition start =
|
||||
editor->getDocument().startOfWord( editor->getDocument().startOfWord( cursor ) );
|
||||
Vector2f cursorPos( startScroll.x + editor->getXOffsetCol( start ),
|
||||
startScroll.y + cursor.line() * lineHeight + lineHeight );
|
||||
size_t largestString = 0;
|
||||
size_t max = eemin<size_t>( mSuggestionsMaxVisible, suggestions.size() );
|
||||
const SyntaxColorScheme& scheme = editor->getColorScheme();
|
||||
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;
|
||||
@@ -410,31 +542,48 @@ void AutoCompletePlugin::postDraw( UICodeEditor* editor, const Vector2f& startSc
|
||||
eemin<size_t>( mSuggestionsStartIndex + mSuggestionsMaxVisible, suggestions.size() );
|
||||
|
||||
for ( size_t i = mSuggestionsStartIndex; i < maxIndex; i++ )
|
||||
largestString = eemax<size_t>( largestString, editor->getTextWidth( suggestions[i] ) );
|
||||
|
||||
Sizef bar( PixelDensity::dpToPx( 8 ),
|
||||
mBoxRect.getSize().getHeight() *
|
||||
( mSuggestionsMaxVisible / (Float)suggestions.size() ) );
|
||||
largestString = eemax<size_t>( largestString, editor->getTextWidth( suggestions[i].text ) );
|
||||
|
||||
Sizef bar( PixelDensity::dpToPxI( 6 ),
|
||||
mRowHeight * max * ( mSuggestionsMaxVisible / (Float)suggestions.size() ) );
|
||||
Sizef iconSpace( PixelDensity::dpToPxI( 16 ), mRowHeight );
|
||||
mBoxRect = Rectf( Vector2f( cursorPos.x, cursorPos.y ) - editor->getScreenPos(),
|
||||
Sizef( largestString + mBoxPadding.Left + mBoxPadding.Right + bar.getWidth(),
|
||||
Sizef( largestString + mBoxPadding.Left + mBoxPadding.Right +
|
||||
iconSpace.getWidth() + bar.getWidth(),
|
||||
mRowHeight * max ) );
|
||||
|
||||
size_t count = 0;
|
||||
Rectf boxRect( { mBoxRect.getPosition() + editor->getScreenPos(), mBoxRect.getSize() } );
|
||||
primitives.setColor( Color( normalStyle.background ).blendAlpha( editor->getAlpha() ) );
|
||||
primitives.drawRoundedRectangle( boxRect, 0.f, Vector2f::One, 6 );
|
||||
|
||||
for ( size_t i = mSuggestionsStartIndex; i < maxIndex; i++ ) {
|
||||
if ( mSuggestionIndex == (int)i ) {
|
||||
primitives.setColor(
|
||||
Color( selectedStyle.background ).blendAlpha( editor->getAlpha() ) );
|
||||
primitives.drawRoundedRectangle(
|
||||
Rectf( Vector2f( cursorPos.x, cursorPos.y + mRowHeight * count ),
|
||||
Sizef( mBoxRect.getWidth(), mRowHeight ) ),
|
||||
0.f, Vector2f::One, 6 );
|
||||
}
|
||||
Text text( "", editor->getFont(), editor->getFontSize() );
|
||||
text.setFillColor( mSuggestionIndex == (int)i ? selectedStyle.color : normalStyle.color );
|
||||
text.setStyle( mSuggestionIndex == (int)i ? selectedStyle.style : normalStyle.style );
|
||||
text.setString( suggestions[i] );
|
||||
primitives.setColor(
|
||||
Color( mSuggestionIndex == (int)i ? selectedStyle.background : normalStyle.background )
|
||||
.blendAlpha( editor->getAlpha() ) );
|
||||
primitives.drawRectangle(
|
||||
Rectf( Vector2f( cursorPos.x, cursorPos.y + mRowHeight * count ),
|
||||
Sizef( largestString + mBoxPadding.Left + mBoxPadding.Right + bar.getWidth(),
|
||||
mRowHeight ) ) );
|
||||
text.draw( cursorPos.x + mBoxPadding.Left,
|
||||
text.setString( suggestions[i].text );
|
||||
|
||||
text.draw( cursorPos.x + iconSpace.getWidth() + mBoxPadding.Left,
|
||||
cursorPos.y + mRowHeight * count + mBoxPadding.Top );
|
||||
|
||||
Drawable* icon = editor->getUISceneNode()->findIconDrawable(
|
||||
LSPCompletionItemHelper::toIconString( suggestions[i].kind ),
|
||||
PixelDensity::dpToPxI( 12 ) );
|
||||
|
||||
if ( icon ) {
|
||||
Vector2f padding(
|
||||
eefloor( ( iconSpace.getWidth() - icon->getSize().getWidth() ) * 0.5f ),
|
||||
eefloor( ( iconSpace.getHeight() - icon->getSize().getHeight() ) * 0.5f ) );
|
||||
icon->draw( { cursorPos.x + padding.x, cursorPos.y + mRowHeight * count + padding.y } );
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
@@ -442,14 +591,19 @@ void AutoCompletePlugin::postDraw( UICodeEditor* editor, const Vector2f& startSc
|
||||
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 } );
|
||||
Float yPos =
|
||||
mSuggestionsStartIndex > 0
|
||||
? mSuggestionsStartIndex / (Float)( suggestions.size() - mSuggestionsMaxVisible )
|
||||
: 0;
|
||||
Rectf barRect( { Vector2f( cursorPos.x + mBoxRect.getWidth() - bar.getWidth(),
|
||||
cursorPos.y + ( mBoxRect.getHeight() - bar.getHeight() ) * yPos ),
|
||||
bar } );
|
||||
if ( bar.getHeight() < 8 ) {
|
||||
primitives.drawRectangle( barRect );
|
||||
} else {
|
||||
primitives.drawRoundedRectangle( barRect, 0, Vector2f::One,
|
||||
(int)eefloor( bar.getWidth() * 0.5f ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool AutoCompletePlugin::onMouseDown( UICodeEditor* editor, const Vector2i& position,
|
||||
@@ -457,25 +611,34 @@ bool AutoCompletePlugin::onMouseDown( UICodeEditor* editor, const Vector2i& posi
|
||||
if ( mSuggestions.empty() || !mSuggestionsEditor || mSuggestionsEditor != editor ||
|
||||
!( flags & EE_BUTTON_LMASK ) )
|
||||
return false;
|
||||
|
||||
Vector2f localPos( editor->convertToNodeSpace( position.asFloat() ) );
|
||||
if ( mBoxRect.contains( localPos ) )
|
||||
if ( mBoxRect.contains( localPos ) ) {
|
||||
localPos -= { mBoxRect.Left, mBoxRect.Top };
|
||||
mSuggestionIndex = mSuggestionsStartIndex + localPos.y / mRowHeight;
|
||||
editor->invalidateDraw();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AutoCompletePlugin::onMouseClick( UICodeEditor* editor, const Vector2i& position,
|
||||
const Uint32& flags ) {
|
||||
if ( mSuggestions.empty() || !mSuggestionsEditor || mSuggestionsEditor != editor ||
|
||||
!( flags & EE_BUTTON_LMASK ) )
|
||||
bool AutoCompletePlugin::onMouseUp( UICodeEditor* editor, const Vector2i& position,
|
||||
const Uint32& flags ) {
|
||||
if ( mSuggestions.empty() || !mSuggestionsEditor || mSuggestionsEditor != editor )
|
||||
return false;
|
||||
|
||||
Vector2f localPos( editor->convertToNodeSpace( position.asFloat() ) );
|
||||
if ( mBoxRect.contains( localPos ) ) {
|
||||
localPos -= { mBoxRect.Left, mBoxRect.Top };
|
||||
mSuggestionIndex = localPos.y / mRowHeight;
|
||||
editor->invalidateDraw();
|
||||
return true;
|
||||
if ( flags & EE_BUTTON_WUMASK ) {
|
||||
mSuggestionsStartIndex = eemax( 0, mSuggestionsStartIndex - mSuggestionsMaxVisible );
|
||||
editor->invalidateDraw();
|
||||
return true;
|
||||
} else if ( flags & EE_BUTTON_WDMASK ) {
|
||||
mSuggestionsStartIndex =
|
||||
eemax( 0, eemin( (int)mSuggestions.size() - mSuggestionsMaxVisible,
|
||||
mSuggestionsStartIndex + mSuggestionsMaxVisible ) );
|
||||
editor->invalidateDraw();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -500,10 +663,12 @@ bool AutoCompletePlugin::onMouseMove( UICodeEditor* editor, const Vector2i& posi
|
||||
return false;
|
||||
|
||||
Vector2f localPos( editor->convertToNodeSpace( position.asFloat() ) );
|
||||
if ( mBoxRect.contains( localPos ) )
|
||||
if ( mBoxRect.contains( localPos ) ) {
|
||||
editor->getUISceneNode()->setCursor( Cursor::Hand );
|
||||
else
|
||||
return true;
|
||||
} else {
|
||||
editor->getUISceneNode()->setCursor( !editor->isLocked() ? Cursor::IBeam : Cursor::Arrow );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -551,12 +716,24 @@ void AutoCompletePlugin::resetSuggestions( UICodeEditor* editor ) {
|
||||
Lock l( mSuggestionsMutex );
|
||||
mSuggestionIndex = 0;
|
||||
mSuggestionsStartIndex = 0;
|
||||
mSuggestionsEditor = nullptr;
|
||||
{
|
||||
Lock l( mSuggestionsEditorMutex );
|
||||
mSuggestionsEditor = nullptr;
|
||||
}
|
||||
mSuggestions.clear();
|
||||
if ( editor && editor->hasFocus() )
|
||||
editor->getUISceneNode()->setCursor( !editor->isLocked() ? Cursor::IBeam : Cursor::Arrow );
|
||||
}
|
||||
|
||||
void AutoCompletePlugin::resetSignatureHelp() {
|
||||
mSignatureHelpVisible = false;
|
||||
mSignatureHelpEditor = nullptr;
|
||||
mSignatureHelpPosition = {};
|
||||
mSignatureHelp.signatures.clear();
|
||||
mSignatureHelp.activeSignature = 0;
|
||||
mSignatureHelp.activeParameter = 0;
|
||||
}
|
||||
|
||||
AutoCompletePlugin::SymbolsList AutoCompletePlugin::getDocumentSymbols( TextDocument* doc ) {
|
||||
LuaPattern pattern( mSymbolPattern );
|
||||
AutoCompletePlugin::SymbolsList symbols;
|
||||
@@ -583,17 +760,12 @@ AutoCompletePlugin::SymbolsList AutoCompletePlugin::getDocumentSymbols( TextDocu
|
||||
void AutoCompletePlugin::runUpdateSuggestions( const std::string& symbol,
|
||||
const SymbolsList& symbols, UICodeEditor* editor ) {
|
||||
{
|
||||
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 );
|
||||
{
|
||||
Lock l( mSuggestionsEditorMutex );
|
||||
mSuggestionsEditor = editor;
|
||||
}
|
||||
if ( tryRequestCapabilities( editor ) )
|
||||
requestCodeCompletion( editor );
|
||||
if ( symbol.empty() )
|
||||
return;
|
||||
Lock l( mLangSymbolsMutex );
|
||||
|
||||
@@ -18,7 +18,38 @@ namespace ecode {
|
||||
|
||||
class AutoCompletePlugin : public UICodeEditorPlugin {
|
||||
public:
|
||||
typedef std::vector<std::string> SymbolsList;
|
||||
class Suggestion {
|
||||
public:
|
||||
LSPCompletionItemKind kind{ LSPCompletionItemKind::Text };
|
||||
std::string text;
|
||||
std::string detail;
|
||||
std::string sortText;
|
||||
TextRange range;
|
||||
double score{ 0 };
|
||||
|
||||
void setScore( const double& score ) const {
|
||||
const_cast<Suggestion*>( this )->score = score;
|
||||
}
|
||||
|
||||
Suggestion( const std::string& text ) : text( text ), sortText( text ) {}
|
||||
|
||||
Suggestion( const LSPCompletionItemKind& kind, const std::string& text,
|
||||
const std::string& detail, const std::string& sortText,
|
||||
const TextRange& range = {} ) :
|
||||
kind( kind ),
|
||||
text( text ),
|
||||
detail( detail ),
|
||||
sortText( sortText.empty() ? text : sortText ),
|
||||
range( range ){};
|
||||
|
||||
bool operator<( const Suggestion& other ) { return getCmpStr() < other.getCmpStr(); }
|
||||
|
||||
bool operator==( const Suggestion& other ) { return text == other.text; }
|
||||
|
||||
protected:
|
||||
const std::string* getCmpStr() const { return !sortText.empty() ? &sortText : &text; }
|
||||
};
|
||||
typedef std::vector<Suggestion> SymbolsList;
|
||||
|
||||
static PluginDefinition Definition() {
|
||||
return { "autocomplete",
|
||||
@@ -29,7 +60,7 @@ class AutoCompletePlugin : public UICodeEditorPlugin {
|
||||
{ 0, 1, 0 } };
|
||||
}
|
||||
|
||||
static UICodeEditorPlugin* New( const PluginManager* pluginManager );
|
||||
static UICodeEditorPlugin* New( PluginManager* pluginManager );
|
||||
|
||||
virtual ~AutoCompletePlugin();
|
||||
|
||||
@@ -49,7 +80,7 @@ class AutoCompletePlugin : public UICodeEditorPlugin {
|
||||
void postDraw( UICodeEditor*, const Vector2f& startScroll, const Float& lineHeight,
|
||||
const TextPosition& cursor );
|
||||
bool onMouseDown( UICodeEditor*, const Vector2i&, const Uint32& );
|
||||
bool onMouseClick( UICodeEditor*, const Vector2i&, const Uint32& );
|
||||
bool onMouseUp( UICodeEditor*, const Vector2i&, const Uint32& );
|
||||
bool onMouseDoubleClick( UICodeEditor*, const Vector2i&, const Uint32& );
|
||||
bool onMouseMove( UICodeEditor*, const Vector2i&, const Uint32& );
|
||||
|
||||
@@ -74,13 +105,7 @@ 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 };
|
||||
PluginManager* mManager{ nullptr };
|
||||
std::string mSymbolPattern;
|
||||
Rectf mBoxPadding;
|
||||
std::shared_ptr<ThreadPool> mPool;
|
||||
@@ -95,6 +120,7 @@ class AutoCompletePlugin : public UICodeEditorPlugin {
|
||||
bool mDirty{ false };
|
||||
bool mClosing{ false };
|
||||
bool mReplacing{ false };
|
||||
bool mSignatureHelpVisible{ false };
|
||||
struct DocCache {
|
||||
Uint64 changeId{ static_cast<Uint64>( -1 ) };
|
||||
SymbolsList symbols;
|
||||
@@ -103,18 +129,22 @@ class AutoCompletePlugin : public UICodeEditorPlugin {
|
||||
std::unordered_map<std::string, SymbolsList> mLangCache;
|
||||
SymbolsList mLangDirty;
|
||||
|
||||
std::vector<std::string> mSuggestions;
|
||||
std::vector<Suggestion> mSuggestions;
|
||||
Mutex mSuggestionsEditorMutex;
|
||||
UICodeEditor* mSuggestionsEditor{ nullptr };
|
||||
UICodeEditor* mSignatureHelpEditor{ nullptr };
|
||||
Int32 mSuggestionIndex{ 0 };
|
||||
Int32 mSuggestionsMaxVisible{ 8 };
|
||||
Int32 mSuggestionsStartIndex{ 0 };
|
||||
std::map<std::string, LSPServerCapabilities> mCapabilities;
|
||||
Mutex mCapabilitiesMutex;
|
||||
LSPSignatureHelp mSignatureHelp;
|
||||
TextPosition mSignatureHelpPosition;
|
||||
|
||||
Float mRowHeight{ 0 };
|
||||
Rectf mBoxRect;
|
||||
|
||||
AutoCompletePlugin( const PluginManager* pluginManager );
|
||||
AutoCompletePlugin( PluginManager* pluginManager );
|
||||
|
||||
void resetSuggestions( UICodeEditor* editor );
|
||||
|
||||
@@ -136,6 +166,16 @@ class AutoCompletePlugin : public UICodeEditorPlugin {
|
||||
PluginRequestHandle processResponse( const PluginMessage& msg );
|
||||
|
||||
bool tryRequestCapabilities( UICodeEditor* editor );
|
||||
|
||||
void requestCodeCompletion( UICodeEditor* editor );
|
||||
|
||||
void requestSignatureHelp( UICodeEditor* editor );
|
||||
|
||||
PluginRequestHandle processCodeCompletion( const std::vector<LSPCompletionItem>& completion );
|
||||
|
||||
PluginRequestHandle processSignatureHelp( const LSPSignatureHelp& signatureHelp );
|
||||
|
||||
void resetSignatureHelp();
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -22,11 +22,11 @@ namespace ecode {
|
||||
#define FORMATTER_THREADED 0
|
||||
#endif
|
||||
|
||||
UICodeEditorPlugin* FormatterPlugin::New( const PluginManager* pluginManager ) {
|
||||
UICodeEditorPlugin* FormatterPlugin::New( PluginManager* pluginManager ) {
|
||||
return eeNew( FormatterPlugin, ( pluginManager ) );
|
||||
}
|
||||
|
||||
FormatterPlugin::FormatterPlugin( const PluginManager* pluginManager ) :
|
||||
FormatterPlugin::FormatterPlugin( PluginManager* pluginManager ) :
|
||||
mPool( pluginManager->getThreadPool() ) {
|
||||
#if FORMATTER_THREADED
|
||||
mPool->run( [&, pluginManager] { load( pluginManager ); }, [] {} );
|
||||
@@ -183,7 +183,7 @@ void FormatterPlugin::loadFormatterConfig( const std::string& path ) {
|
||||
}
|
||||
}
|
||||
|
||||
void FormatterPlugin::load( const PluginManager* pluginManager ) {
|
||||
void FormatterPlugin::load( PluginManager* pluginManager ) {
|
||||
registerNativeFormatters();
|
||||
|
||||
std::vector<std::string> paths;
|
||||
|
||||
@@ -29,7 +29,7 @@ class FormatterPlugin : public UICodeEditorPlugin {
|
||||
{ 0, 1, 0 } };
|
||||
}
|
||||
|
||||
static UICodeEditorPlugin* New( const PluginManager* pluginManager );
|
||||
static UICodeEditorPlugin* New( PluginManager* pluginManager );
|
||||
|
||||
virtual ~FormatterPlugin();
|
||||
|
||||
@@ -84,9 +84,9 @@ class FormatterPlugin : public UICodeEditorPlugin {
|
||||
bool mReady{ false };
|
||||
Uint32 mOnDocumentSaveCb{ 0 };
|
||||
|
||||
FormatterPlugin( const PluginManager* pluginManager );
|
||||
FormatterPlugin( PluginManager* pluginManager );
|
||||
|
||||
void load( const PluginManager* pluginManager );
|
||||
void load( PluginManager* pluginManager );
|
||||
|
||||
void loadFormatterConfig( const std::string& path );
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@ namespace ecode {
|
||||
#define LINTER_THREADED 0
|
||||
#endif
|
||||
|
||||
UICodeEditorPlugin* LinterPlugin::New( const PluginManager* pluginManager ) {
|
||||
UICodeEditorPlugin* LinterPlugin::New( PluginManager* pluginManager ) {
|
||||
return eeNew( LinterPlugin, ( pluginManager ) );
|
||||
}
|
||||
|
||||
LinterPlugin::LinterPlugin( const PluginManager* pluginManager ) :
|
||||
LinterPlugin::LinterPlugin( PluginManager* pluginManager ) :
|
||||
mManager( pluginManager ), mPool( pluginManager->getThreadPool() ) {
|
||||
#if LINTER_THREADED
|
||||
mPool->run( [&, pluginManager] { load( pluginManager ); }, [] {} );
|
||||
@@ -301,7 +301,7 @@ TextDocument* LinterPlugin::getDocumentFromURI( const URI& uri ) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void LinterPlugin::load( const PluginManager* pluginManager ) {
|
||||
void LinterPlugin::load( PluginManager* pluginManager ) {
|
||||
pluginManager->subscribeMessages( this, [&]( const auto& notification ) -> PluginRequestHandle {
|
||||
return processMessage( notification );
|
||||
} );
|
||||
@@ -727,6 +727,7 @@ bool LinterPlugin::onMouseMove( UICodeEditor* editor, const Vector2i& pos, const
|
||||
mHoveringMatch = true;
|
||||
editor->runOnMainThread( [&, editor] {
|
||||
editor->setTooltipText( match.text );
|
||||
editor->getTooltip()->setHorizontalAlign( UI_HALIGN_LEFT );
|
||||
editor->getTooltip()->setDontAutoHideOnMouseMove( true );
|
||||
editor->getTooltip()->setPixelsPosition( Vector2f( pos.x, pos.y ) );
|
||||
if ( !editor->getTooltip()->isVisible() )
|
||||
|
||||
@@ -55,7 +55,7 @@ class LinterPlugin : public UICodeEditorPlugin {
|
||||
LinterPlugin::New,
|
||||
{ 0, 1, 0 } };
|
||||
}
|
||||
static UICodeEditorPlugin* New( const PluginManager* pluginManager );
|
||||
static UICodeEditorPlugin* New( PluginManager* pluginManager );
|
||||
|
||||
virtual ~LinterPlugin();
|
||||
|
||||
@@ -96,7 +96,7 @@ class LinterPlugin : public UICodeEditorPlugin {
|
||||
void setEnableLSPDiagnostics( bool enableLSPDiagnostics );
|
||||
|
||||
protected:
|
||||
const PluginManager* mManager{ nullptr };
|
||||
PluginManager* mManager{ nullptr };
|
||||
std::shared_ptr<ThreadPool> mPool;
|
||||
std::vector<Linter> mLinters;
|
||||
std::unordered_map<UICodeEditor*, std::vector<Uint32>> mEditors;
|
||||
@@ -119,9 +119,9 @@ class LinterPlugin : public UICodeEditorPlugin {
|
||||
std::set<std::string> mLanguagesDisabled;
|
||||
std::set<std::string> mLSPLanguagesDisabled;
|
||||
|
||||
LinterPlugin( const PluginManager* pluginManager );
|
||||
LinterPlugin( PluginManager* pluginManager );
|
||||
|
||||
void load( const PluginManager* pluginManager );
|
||||
void load( PluginManager* pluginManager );
|
||||
|
||||
void lintDoc( std::shared_ptr<TextDocument> doc );
|
||||
|
||||
|
||||
@@ -13,11 +13,11 @@ using json = nlohmann::json;
|
||||
|
||||
namespace ecode {
|
||||
|
||||
UICodeEditorPlugin* LSPClientPlugin::New( const PluginManager* pluginManager ) {
|
||||
UICodeEditorPlugin* LSPClientPlugin::New( PluginManager* pluginManager ) {
|
||||
return eeNew( LSPClientPlugin, ( pluginManager ) );
|
||||
}
|
||||
|
||||
LSPClientPlugin::LSPClientPlugin( const PluginManager* pluginManager ) :
|
||||
LSPClientPlugin::LSPClientPlugin( PluginManager* pluginManager ) :
|
||||
mManager( pluginManager ), mThreadPool( pluginManager->getThreadPool() ) {
|
||||
mThreadPool->run( [&, pluginManager] { load( pluginManager ); }, [] {} );
|
||||
}
|
||||
@@ -48,11 +48,12 @@ void LSPClientPlugin::update( UICodeEditor* ) {
|
||||
mClientManager.updateDirty();
|
||||
}
|
||||
|
||||
PluginRequestHandle LSPClientPlugin::processCodeCompletionRequest( const PluginMessage& msg ) {
|
||||
if ( !msg.isRequest() || !msg.isJSON() )
|
||||
return {};
|
||||
struct LSPPositionAndServer {
|
||||
LSPPosition loc;
|
||||
LSPClientServer* server{ nullptr };
|
||||
};
|
||||
|
||||
const auto& data = msg.asJSON();
|
||||
LSPPositionAndServer getLSPLocationFromJSON( LSPClientServerManager& manager, const json& data ) {
|
||||
if ( !data.contains( "uri" ) || !data.contains( "position" ) )
|
||||
return {};
|
||||
|
||||
@@ -61,12 +62,23 @@ PluginRequestHandle LSPClientPlugin::processCodeCompletionRequest( const PluginM
|
||||
return {};
|
||||
|
||||
URI uri( data["uri"] );
|
||||
auto server = mClientManager.getOneLSPClientServer( uri );
|
||||
auto server = manager.getOneLSPClientServer( uri );
|
||||
if ( !server )
|
||||
return {};
|
||||
return { { uri, position }, server };
|
||||
}
|
||||
|
||||
auto ret = server->documentCompletion(
|
||||
uri, position, [&]( const PluginIDType& id, const std::vector<LSPCompletionItem>& items ) {
|
||||
PluginRequestHandle LSPClientPlugin::processCodeCompletionRequest( const PluginMessage& msg ) {
|
||||
if ( !msg.isRequest() || !msg.isJSON() )
|
||||
return {};
|
||||
|
||||
auto res = getLSPLocationFromJSON( mClientManager, msg.asJSON() );
|
||||
if ( !res.server )
|
||||
return {};
|
||||
|
||||
auto ret = res.server->documentCompletion(
|
||||
res.loc.uri, res.loc.pos,
|
||||
[&]( const PluginIDType& id, const std::vector<LSPCompletionItem>& items ) {
|
||||
mManager->sendResponse( this, PluginMessageType::CodeCompletion,
|
||||
PluginMessageFormat::CodeCompletion, &items, id );
|
||||
} );
|
||||
@@ -74,6 +86,23 @@ PluginRequestHandle LSPClientPlugin::processCodeCompletionRequest( const PluginM
|
||||
return ret;
|
||||
}
|
||||
|
||||
PluginRequestHandle LSPClientPlugin::processSignatureHelpRequest( const PluginMessage& msg ) {
|
||||
if ( !msg.isRequest() || !msg.isJSON() )
|
||||
return {};
|
||||
|
||||
auto res = getLSPLocationFromJSON( mClientManager, msg.asJSON() );
|
||||
if ( !res.server )
|
||||
return {};
|
||||
|
||||
auto ret = res.server->signatureHelp(
|
||||
res.loc.uri, res.loc.pos, [&]( const PluginIDType& id, const LSPSignatureHelp& data ) {
|
||||
mManager->sendResponse( this, PluginMessageType::SignatureHelp,
|
||||
PluginMessageFormat::SignatureHelp, &data, id );
|
||||
} );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PluginRequestHandle LSPClientPlugin::processMessage( const PluginMessage& msg ) {
|
||||
switch ( msg.type ) {
|
||||
case PluginMessageType::WorkspaceFolderChanged: {
|
||||
@@ -86,6 +115,12 @@ PluginRequestHandle LSPClientPlugin::processMessage( const PluginMessage& msg )
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
case PluginMessageType::SignatureHelp: {
|
||||
auto ret = processSignatureHelpRequest( msg );
|
||||
if ( !ret.isEmpty() )
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
case PluginMessageType::LanguageServerCapabilities: {
|
||||
if ( msg.isRequest() && msg.isJSON() ) {
|
||||
const auto& data = msg.asJSON();
|
||||
@@ -108,7 +143,7 @@ PluginRequestHandle LSPClientPlugin::processMessage( const PluginMessage& msg )
|
||||
return PluginRequestHandle::empty();
|
||||
}
|
||||
|
||||
void LSPClientPlugin::load( const PluginManager* pluginManager ) {
|
||||
void LSPClientPlugin::load( PluginManager* pluginManager ) {
|
||||
pluginManager->subscribeMessages( this, [&]( const auto& notification ) -> PluginRequestHandle {
|
||||
return processMessage( notification );
|
||||
} );
|
||||
@@ -361,7 +396,7 @@ void LSPClientPlugin::onUnregister( UICodeEditor* editor ) {
|
||||
mDocs.erase( doc );
|
||||
}
|
||||
|
||||
const PluginManager* LSPClientPlugin::getManager() const {
|
||||
PluginManager* LSPClientPlugin::getManager() const {
|
||||
return mManager;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class LSPClientPlugin : public UICodeEditorPlugin {
|
||||
{ 0, 0, 1 } };
|
||||
}
|
||||
|
||||
static UICodeEditorPlugin* New( const PluginManager* pluginManager );
|
||||
static UICodeEditorPlugin* New( PluginManager* pluginManager );
|
||||
|
||||
virtual ~LSPClientPlugin();
|
||||
|
||||
@@ -49,7 +49,7 @@ class LSPClientPlugin : public UICodeEditorPlugin {
|
||||
|
||||
const std::unordered_map<UICodeEditor*, TextDocument*>& getEditorDocs() { return mEditorDocs; };
|
||||
|
||||
const PluginManager* getManager() const;
|
||||
PluginManager* getManager() const;
|
||||
|
||||
virtual bool onCreateContextMenu( UICodeEditor* editor, UIPopUpMenu* menu,
|
||||
const Vector2i& position, const Uint32& flags );
|
||||
@@ -67,7 +67,7 @@ class LSPClientPlugin : public UICodeEditorPlugin {
|
||||
const LSPClientServerManager& getClientManager() const;
|
||||
|
||||
protected:
|
||||
const PluginManager* mManager{ nullptr };
|
||||
PluginManager* mManager{ nullptr };
|
||||
std::shared_ptr<ThreadPool> mThreadPool;
|
||||
Clock mClock;
|
||||
Mutex mDocMutex;
|
||||
@@ -85,9 +85,9 @@ class LSPClientPlugin : public UICodeEditorPlugin {
|
||||
LSPHover mCurrentHover;
|
||||
Time mHoverDelay{ Seconds( 1.f ) };
|
||||
|
||||
LSPClientPlugin( const PluginManager* pluginManager );
|
||||
LSPClientPlugin( PluginManager* pluginManager );
|
||||
|
||||
void load( const PluginManager* pluginManager );
|
||||
void load( PluginManager* pluginManager );
|
||||
|
||||
void loadLSPConfig( std::vector<LSPDefinition>& lsps, const std::string& path );
|
||||
|
||||
@@ -97,6 +97,8 @@ class LSPClientPlugin : public UICodeEditorPlugin {
|
||||
PluginRequestHandle processMessage( const PluginMessage& msg );
|
||||
|
||||
PluginRequestHandle processCodeCompletionRequest( const PluginMessage& msg );
|
||||
|
||||
PluginRequestHandle processSignatureHelpRequest( const PluginMessage& msg );
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -631,29 +631,33 @@ static std::vector<LSPCompletionItem> parseDocumentCompletion( const json& resul
|
||||
std::vector<LSPCompletionItem> ret;
|
||||
if ( result.empty() )
|
||||
return {};
|
||||
const json& items =
|
||||
( result.is_object() && result.contains( "items" ) ) ? result["items"] : result;
|
||||
try {
|
||||
const json& items =
|
||||
( result.is_object() && result.contains( "items" ) ) ? result["items"] : result;
|
||||
|
||||
for ( const auto& item : items ) {
|
||||
auto label = item.value( MEMBER_LABEL, "" );
|
||||
auto detail = item.value( MEMBER_DETAIL, "" );
|
||||
LSPMarkupContent doc = item.contains( MEMBER_DOCUMENTATION )
|
||||
? parseMarkupContent( item.at( MEMBER_DOCUMENTATION ) )
|
||||
: LSPMarkupContent{};
|
||||
auto filterText = item.value( "filterText", label );
|
||||
auto insertText = item.value( "insertText", label );
|
||||
auto sortText = item.value( "sortText", label );
|
||||
LSPTextEdit textEdit;
|
||||
if ( item.contains( "textEdit" ) )
|
||||
textEdit = parseTextEdit( item["textEdit"] );
|
||||
auto kind = static_cast<LSPCompletionItemKind>( item.value( MEMBER_KIND, 1 ) );
|
||||
const std::vector<LSPTextEdit> additionalTextEdits =
|
||||
item.contains( "additionalTextEdits" )
|
||||
? parseTextEditArray( item.at( "additionalTextEdits" ) )
|
||||
: std::vector<LSPTextEdit>{};
|
||||
for ( const auto& item : items ) {
|
||||
auto label = item.value( MEMBER_LABEL, "" );
|
||||
auto detail = item.value( MEMBER_DETAIL, "" );
|
||||
LSPMarkupContent doc = item.contains( MEMBER_DOCUMENTATION )
|
||||
? parseMarkupContent( item.at( MEMBER_DOCUMENTATION ) )
|
||||
: LSPMarkupContent{};
|
||||
auto filterText = item.value( "filterText", label );
|
||||
auto insertText = item.value( "insertText", label );
|
||||
auto sortText = item.value( "sortText", label );
|
||||
LSPTextEdit textEdit;
|
||||
if ( item.contains( "textEdit" ) )
|
||||
textEdit = parseTextEdit( item["textEdit"] );
|
||||
auto kind = static_cast<LSPCompletionItemKind>( item.value( MEMBER_KIND, 1 ) );
|
||||
const std::vector<LSPTextEdit> additionalTextEdits =
|
||||
item.contains( "additionalTextEdits" )
|
||||
? parseTextEditArray( item.at( "additionalTextEdits" ) )
|
||||
: std::vector<LSPTextEdit>{};
|
||||
|
||||
ret.push_back( { label, kind, detail, doc, sortText, insertText, filterText, textEdit,
|
||||
additionalTextEdits } );
|
||||
ret.push_back( { label, kind, detail, doc, sortText, insertText, filterText, textEdit,
|
||||
additionalTextEdits } );
|
||||
}
|
||||
} catch ( const json::exception& err ) {
|
||||
Log::warning( "Error parsing parseDocumentCompletion: %s", err.what() );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -662,7 +666,8 @@ static LSPSignatureInformation parseSignatureInformation( const json& json ) {
|
||||
LSPSignatureInformation info;
|
||||
|
||||
info.label = json.value( MEMBER_LABEL, "" );
|
||||
info.documentation = parseMarkupContent( json.value( MEMBER_DOCUMENTATION, {} ) );
|
||||
if ( json.contains( MEMBER_DOCUMENTATION ) )
|
||||
info.documentation = parseMarkupContent( json.at( MEMBER_DOCUMENTATION ) );
|
||||
const auto& params = json.at( "parameters" );
|
||||
for ( const auto& par : params ) {
|
||||
auto label = par.at( MEMBER_LABEL );
|
||||
@@ -692,16 +697,21 @@ static LSPSignatureInformation parseSignatureInformation( const json& json ) {
|
||||
|
||||
static LSPSignatureHelp parseSignatureHelp( const json& sig ) {
|
||||
LSPSignatureHelp ret;
|
||||
const auto& sigInfos = sig.at( "signatures" );
|
||||
for ( const auto& info : sigInfos )
|
||||
ret.signatures.push_back( parseSignatureInformation( info ) );
|
||||
ret.activeSignature = sig.value( "activeSignature", 0 );
|
||||
ret.activeParameter = sig.value( "activeParameter", 0 );
|
||||
ret.activeSignature = eemin( eemax( ret.activeSignature, 0 ), (int)ret.signatures.size() );
|
||||
ret.activeParameter = eemax( ret.activeParameter, 0 );
|
||||
if ( !ret.signatures.empty() ) {
|
||||
ret.activeParameter = eemin(
|
||||
ret.activeParameter, (int)ret.signatures.at( ret.activeSignature ).parameters.size() );
|
||||
try {
|
||||
const auto& sigInfos = sig.at( "signatures" );
|
||||
for ( const auto& info : sigInfos )
|
||||
ret.signatures.push_back( parseSignatureInformation( info ) );
|
||||
ret.activeSignature = sig.value( "activeSignature", 0 );
|
||||
ret.activeParameter = sig.value( "activeParameter", 0 );
|
||||
ret.activeSignature = eemin( eemax( ret.activeSignature, 0 ), (int)ret.signatures.size() );
|
||||
ret.activeParameter = eemax( ret.activeParameter, 0 );
|
||||
if ( !ret.signatures.empty() ) {
|
||||
ret.activeParameter =
|
||||
eemin( ret.activeParameter,
|
||||
(int)ret.signatures.at( ret.activeSignature ).parameters.size() );
|
||||
}
|
||||
} catch ( const json::exception& err ) {
|
||||
Log::warning( "Error parsing parseSignatureHelp: %s", err.what() );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1293,8 +1303,7 @@ LSPClientServer::LSPRequestHandle LSPClientServer::switchSourceHeader( const URI
|
||||
return send( newRequest( "textDocument/switchSourceHeader", textDocumentURI( document ) ),
|
||||
[this]( const IdType&, json res ) {
|
||||
if ( res.is_string() ) {
|
||||
mManager->goToLocation(
|
||||
{ res.get<std::string>(), TextRange{ { 0, 0 }, { 0, 0 } } } );
|
||||
mManager->goToLocation( { res.get<std::string>(), TextRange() } );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace ecode {
|
||||
|
||||
LSPClientServerManager::LSPClientServerManager() {}
|
||||
|
||||
void LSPClientServerManager::load( LSPClientPlugin* plugin, const PluginManager* pluginManager,
|
||||
void LSPClientServerManager::load( LSPClientPlugin* plugin, PluginManager* pluginManager,
|
||||
std::vector<LSPDefinition>&& lsps ) {
|
||||
mPlugin = plugin;
|
||||
mPluginManager = pluginManager;
|
||||
@@ -117,15 +117,17 @@ void LSPClientServerManager::goToLocation( const LSPLocation& loc ) {
|
||||
std::string path( loc.uri.getPath() );
|
||||
FileInfo fileInfo( path );
|
||||
if ( fileInfo.exists() && fileInfo.isRegularFile() ) {
|
||||
splitter->loadAsyncFileFromPathInNewTab( path, mThreadPool,
|
||||
[loc]( UICodeEditor* editor, auto ) {
|
||||
editor->goToLine( loc.range.start() );
|
||||
editor->setFocus();
|
||||
} );
|
||||
splitter->loadAsyncFileFromPathInNewTab(
|
||||
path, mThreadPool, [loc]( UICodeEditor* editor, auto ) {
|
||||
if ( loc.range.isValid() )
|
||||
editor->goToLine( loc.range.start() );
|
||||
editor->setFocus();
|
||||
} );
|
||||
}
|
||||
} else {
|
||||
tab->getTabWidget()->setTabSelected( tab );
|
||||
splitter->editorFromTab( tab )->goToLine( loc.range.start() );
|
||||
if ( loc.range.isValid() )
|
||||
splitter->editorFromTab( tab )->goToLine( loc.range.start() );
|
||||
splitter->editorFromTab( tab )->setFocus();
|
||||
}
|
||||
} );
|
||||
@@ -169,7 +171,7 @@ void LSPClientServerManager::getAndGoToLocation( const std::shared_ptr<TextDocum
|
||||
server->getAndGoToLocation( doc->getURI(), doc->getSelection().start(), search );
|
||||
}
|
||||
|
||||
const PluginManager* LSPClientServerManager::getPluginManager() const {
|
||||
PluginManager* LSPClientServerManager::getPluginManager() const {
|
||||
return mPluginManager;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@ class LSPClientServerManager {
|
||||
public:
|
||||
LSPClientServerManager();
|
||||
|
||||
void load( LSPClientPlugin*, const PluginManager* pluginManager,
|
||||
std::vector<LSPDefinition>&& lsps );
|
||||
void load( LSPClientPlugin*, PluginManager* pluginManager, std::vector<LSPDefinition>&& lsps );
|
||||
|
||||
// async
|
||||
void run( const std::shared_ptr<TextDocument>& doc );
|
||||
@@ -55,13 +54,13 @@ class LSPClientServerManager {
|
||||
|
||||
void getAndGoToLocation( const std::shared_ptr<TextDocument>& doc, const std::string& search );
|
||||
|
||||
const PluginManager* getPluginManager() const;
|
||||
PluginManager* getPluginManager() const;
|
||||
|
||||
LSPClientPlugin* getPlugin() const;
|
||||
|
||||
protected:
|
||||
friend class LSPClientServer;
|
||||
const PluginManager* mPluginManager{ nullptr };
|
||||
PluginManager* mPluginManager{ nullptr };
|
||||
LSPClientPlugin* mPlugin{ nullptr };
|
||||
std::shared_ptr<ThreadPool> mThreadPool;
|
||||
std::map<String::HashType, std::unique_ptr<LSPClientServer>> mClients;
|
||||
|
||||
@@ -31,6 +31,11 @@ enum class LSPErrorCode {
|
||||
ContentModified = -32801
|
||||
};
|
||||
|
||||
struct LSPPosition {
|
||||
URI uri;
|
||||
TextPosition pos;
|
||||
};
|
||||
|
||||
struct LSPLocation {
|
||||
URI uri;
|
||||
TextRange range;
|
||||
@@ -287,6 +292,65 @@ enum class LSPCompletionItemKind {
|
||||
TypeParameter = 25,
|
||||
};
|
||||
|
||||
class LSPCompletionItemHelper {
|
||||
public:
|
||||
static std::string toIconString( const LSPCompletionItemKind& kind ) {
|
||||
switch ( kind ) {
|
||||
case LSPCompletionItemKind::Text:
|
||||
return "symbol-text";
|
||||
case LSPCompletionItemKind::Method:
|
||||
return "symbol-method";
|
||||
case LSPCompletionItemKind::Function:
|
||||
return "symbol-function";
|
||||
case LSPCompletionItemKind::Constructor:
|
||||
return "symbol-constructor";
|
||||
case LSPCompletionItemKind::Field:
|
||||
return "symbol-field";
|
||||
case LSPCompletionItemKind::Variable:
|
||||
return "symbol-variable";
|
||||
case LSPCompletionItemKind::Class:
|
||||
return "symbol-class";
|
||||
case LSPCompletionItemKind::Interface:
|
||||
return "symbol-interface";
|
||||
case LSPCompletionItemKind::Module:
|
||||
return "symbol-module";
|
||||
case LSPCompletionItemKind::Property:
|
||||
return "symbol-property";
|
||||
case LSPCompletionItemKind::Unit:
|
||||
return "symbol-unit";
|
||||
case LSPCompletionItemKind::Value:
|
||||
return "symbol-value";
|
||||
case LSPCompletionItemKind::Enum:
|
||||
return "symbol-enum";
|
||||
case LSPCompletionItemKind::Keyword:
|
||||
return "symbol-keyword";
|
||||
case LSPCompletionItemKind::Snippet:
|
||||
return "symbol-snippet";
|
||||
case LSPCompletionItemKind::Color:
|
||||
return "symbol-color";
|
||||
case LSPCompletionItemKind::File:
|
||||
return "symbol-file";
|
||||
case LSPCompletionItemKind::Reference:
|
||||
return "symbol-reference";
|
||||
case LSPCompletionItemKind::Folder:
|
||||
return "symbol-folder";
|
||||
case LSPCompletionItemKind::EnumMember:
|
||||
return "symbol-enum-member";
|
||||
case LSPCompletionItemKind::Constant:
|
||||
return "symbol-constant";
|
||||
case LSPCompletionItemKind::Struct:
|
||||
return "symbol-struct";
|
||||
case LSPCompletionItemKind::Event:
|
||||
return "symbol-event";
|
||||
case LSPCompletionItemKind::Operator:
|
||||
return "symbol-operator";
|
||||
case LSPCompletionItemKind::TypeParameter:
|
||||
return "symbol-type-parameter";
|
||||
}
|
||||
return "symbol-text";
|
||||
}
|
||||
};
|
||||
|
||||
struct LSPCompletionItem {
|
||||
std::string label;
|
||||
LSPCompletionItemKind kind;
|
||||
|
||||
@@ -41,7 +41,10 @@ bool PluginManager::setEnabled( const std::string& id, bool enable ) {
|
||||
}
|
||||
if ( !enable && plugin != nullptr ) {
|
||||
eeSAFE_DELETE( plugin );
|
||||
mSubscribedPlugins.erase( id );
|
||||
{
|
||||
Lock l( mSubscribedPluginsMutex );
|
||||
mSubscribedPlugins.erase( id );
|
||||
}
|
||||
mPlugins.erase( id );
|
||||
}
|
||||
return false;
|
||||
@@ -111,10 +114,15 @@ void PluginManager::setWorkspaceFolder( const std::string& workspaceFolder ) {
|
||||
|
||||
PluginRequestHandle PluginManager::sendRequest( UICodeEditorPlugin* pluginWho,
|
||||
PluginMessageType type, PluginMessageFormat format,
|
||||
const void* data ) const {
|
||||
const void* data ) {
|
||||
if ( mClosing )
|
||||
return PluginRequestHandle::empty();
|
||||
for ( const auto& plugin : mSubscribedPlugins ) {
|
||||
SubscribedPlugins subscribedPlugins;
|
||||
{
|
||||
Lock l( mSubscribedPluginsMutex );
|
||||
subscribedPlugins = mSubscribedPlugins;
|
||||
}
|
||||
for ( const auto& plugin : subscribedPlugins ) {
|
||||
if ( pluginWho->getId() != plugin.first ) {
|
||||
auto handle = plugin.second( { type, format, data } );
|
||||
if ( !handle.isEmpty() )
|
||||
@@ -126,36 +134,50 @@ PluginRequestHandle PluginManager::sendRequest( UICodeEditorPlugin* pluginWho,
|
||||
|
||||
void PluginManager::sendResponse( UICodeEditorPlugin* pluginWho, PluginMessageType type,
|
||||
PluginMessageFormat format, const void* data,
|
||||
const PluginIDType& responseID ) const {
|
||||
const PluginIDType& responseID ) {
|
||||
if ( mClosing )
|
||||
return;
|
||||
for ( const auto& plugin : mSubscribedPlugins )
|
||||
SubscribedPlugins subscribedPlugins;
|
||||
{
|
||||
Lock l( mSubscribedPluginsMutex );
|
||||
subscribedPlugins = mSubscribedPlugins;
|
||||
}
|
||||
for ( const auto& plugin : subscribedPlugins )
|
||||
if ( pluginWho->getId() != plugin.first )
|
||||
plugin.second( { type, format, data, responseID } );
|
||||
}
|
||||
|
||||
void PluginManager::sendBroadcast( UICodeEditorPlugin* pluginWho, PluginMessageType type,
|
||||
PluginMessageFormat format, const void* data ) const {
|
||||
PluginMessageFormat format, const void* data ) {
|
||||
if ( mClosing )
|
||||
return;
|
||||
for ( const auto& plugin : mSubscribedPlugins )
|
||||
SubscribedPlugins subscribedPlugins;
|
||||
{
|
||||
Lock l( mSubscribedPluginsMutex );
|
||||
subscribedPlugins = mSubscribedPlugins;
|
||||
}
|
||||
for ( const auto& plugin : subscribedPlugins )
|
||||
if ( pluginWho->getId() != plugin.first )
|
||||
plugin.second( { type, format, data, -1 } );
|
||||
}
|
||||
|
||||
void PluginManager::subscribeMessages(
|
||||
UICodeEditorPlugin* plugin,
|
||||
std::function<PluginRequestHandle( const PluginMessage& )> cb ) const {
|
||||
const_cast<PluginManager*>( this )->mSubscribedPlugins[plugin->getId()] = cb;
|
||||
UICodeEditorPlugin* plugin, std::function<PluginRequestHandle( const PluginMessage& )> cb ) {
|
||||
{
|
||||
Lock l( mSubscribedPluginsMutex );
|
||||
mSubscribedPlugins[plugin->getId()] = cb;
|
||||
}
|
||||
if ( !mWorkspaceFolder.empty() ) {
|
||||
json data{ { "folder", mWorkspaceFolder } };
|
||||
cb( { PluginMessageType::WorkspaceFolderChanged, PluginMessageFormat::JSON, &data } );
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::unsubscribeMessages( UICodeEditorPlugin* plugin ) const {
|
||||
if ( !mClosing )
|
||||
const_cast<PluginManager*>( this )->mSubscribedPlugins.erase( plugin->getId() );
|
||||
void PluginManager::unsubscribeMessages( UICodeEditorPlugin* plugin ) {
|
||||
if ( !mClosing ) {
|
||||
Lock l( mSubscribedPluginsMutex );
|
||||
mSubscribedPlugins.erase( plugin->getId() );
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::setSplitter( UICodeEditorSplitter* splitter ) {
|
||||
@@ -166,7 +188,12 @@ void PluginManager::sendBroadcast( const PluginMessageType& notification,
|
||||
const PluginMessageFormat& format, void* data ) {
|
||||
if ( mClosing )
|
||||
return;
|
||||
for ( const auto& plugin : mSubscribedPlugins )
|
||||
SubscribedPlugins subscribedPlugins;
|
||||
{
|
||||
Lock l( mSubscribedPluginsMutex );
|
||||
subscribedPlugins = mSubscribedPlugins;
|
||||
}
|
||||
for ( const auto& plugin : subscribedPlugins )
|
||||
plugin.second( { notification, format, data } );
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace ecode {
|
||||
|
||||
class PluginManager;
|
||||
|
||||
typedef std::function<UICodeEditorPlugin*( const PluginManager* pluginManager )> PluginCreatorFn;
|
||||
typedef std::function<UICodeEditorPlugin*( PluginManager* pluginManager )> PluginCreatorFn;
|
||||
|
||||
#ifdef minor
|
||||
#undef minor
|
||||
@@ -63,10 +63,17 @@ enum class PluginMessageType {
|
||||
// and position
|
||||
LanguageServerCapabilities, // Request the language server capabilities of a language if there
|
||||
// is any available, it will be returned as a broadcast
|
||||
SignatureHelp, // Request the LSP Client to provide function/method signature help
|
||||
Undefined
|
||||
};
|
||||
|
||||
enum class PluginMessageFormat { JSON, Diagnostics, CodeCompletion, LanguageServerCapabilities };
|
||||
enum class PluginMessageFormat {
|
||||
JSON,
|
||||
Diagnostics,
|
||||
CodeCompletion,
|
||||
LanguageServerCapabilities,
|
||||
SignatureHelp
|
||||
};
|
||||
|
||||
using PluginIDType = Int64;
|
||||
|
||||
@@ -94,6 +101,10 @@ struct PluginMessage {
|
||||
return *static_cast<const LSPServerCapabilities*>( data );
|
||||
}
|
||||
|
||||
const LSPSignatureHelp& asSignatureHelp() const {
|
||||
return *static_cast<const LSPSignatureHelp*>( data );
|
||||
}
|
||||
|
||||
bool isResponse() const { return -1 != responseID && 0 != responseID; }
|
||||
|
||||
bool isRequest() const { return -1 != responseID && 0 == responseID; }
|
||||
@@ -168,24 +179,26 @@ class PluginManager {
|
||||
void setWorkspaceFolder( const std::string& workspaceFolder );
|
||||
|
||||
PluginRequestHandle sendRequest( UICodeEditorPlugin* pluginWho, PluginMessageType type,
|
||||
PluginMessageFormat format, const void* data ) const;
|
||||
PluginMessageFormat format, const void* data );
|
||||
|
||||
void sendResponse( UICodeEditorPlugin* pluginWho, PluginMessageType type,
|
||||
PluginMessageFormat format, const void* data,
|
||||
const PluginIDType& responseID ) const;
|
||||
const PluginIDType& responseID );
|
||||
|
||||
void sendBroadcast( UICodeEditorPlugin* pluginWho, PluginMessageType, PluginMessageFormat,
|
||||
const void* data ) const;
|
||||
const void* data );
|
||||
|
||||
void sendBroadcast( const PluginMessageType& notification, const PluginMessageFormat& format,
|
||||
void* data );
|
||||
|
||||
void subscribeMessages( UICodeEditorPlugin* plugin,
|
||||
std::function<PluginRequestHandle( const PluginMessage& )> cb ) const;
|
||||
std::function<PluginRequestHandle( const PluginMessage& )> cb );
|
||||
|
||||
void unsubscribeMessages( UICodeEditorPlugin* plugin ) const;
|
||||
void unsubscribeMessages( UICodeEditorPlugin* plugin );
|
||||
|
||||
protected:
|
||||
using SubscribedPlugins =
|
||||
std::map<std::string, std::function<PluginRequestHandle( const PluginMessage& )>>;
|
||||
friend class App;
|
||||
std::string mResourcesPath;
|
||||
std::string mPluginsPath;
|
||||
@@ -195,8 +208,8 @@ class PluginManager {
|
||||
std::map<std::string, PluginDefinition> mDefinitions;
|
||||
std::shared_ptr<ThreadPool> mThreadPool;
|
||||
UICodeEditorSplitter* mSplitter{ nullptr };
|
||||
std::map<std::string, std::function<PluginRequestHandle( const PluginMessage& )>>
|
||||
mSubscribedPlugins;
|
||||
Mutex mSubscribedPluginsMutex;
|
||||
SubscribedPlugins mSubscribedPlugins;
|
||||
bool mClosing{ false };
|
||||
|
||||
bool hasDefinition( const std::string& id );
|
||||
|
||||
Reference in New Issue
Block a user