mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-06-04 20:46:29 +03:00
Designing the inter-plugin communication protocol.
This commit is contained in:
@@ -249,18 +249,16 @@ void LinterPlugin::setMatches( TextDocument* doc, const MatchOrigin& origin,
|
||||
invalidateEditors( doc );
|
||||
}
|
||||
|
||||
void LinterPlugin::processNotification( const PluginManager::Notification& notification ) {
|
||||
if ( !mEnableLSPDiagnostics ||
|
||||
notification.type != PluginManager::NotificationType::PublishDiagnostics ||
|
||||
notification.format != PluginManager::NotificationFormat::Diagnostics )
|
||||
return;
|
||||
PluginRequestHandle LinterPlugin::processMessage( const PluginMessage& notification ) {
|
||||
if ( !mEnableLSPDiagnostics || notification.type != PluginMessageType::Diagnostics ||
|
||||
notification.format != PluginMessageFormat::Diagnostics )
|
||||
return PluginRequestHandle::empty();
|
||||
const auto& diags = notification.asDiagnostics();
|
||||
TextDocument* doc = getDocumentFromURI( diags.uri );
|
||||
if ( doc == nullptr )
|
||||
return;
|
||||
if ( mLSPLanguagesDisabled.find( String::toLower(
|
||||
doc->getSyntaxDefinition().getLSPName() ) ) != mLSPLanguagesDisabled.end() )
|
||||
return;
|
||||
if ( doc == nullptr ||
|
||||
mLSPLanguagesDisabled.find( String::toLower( doc->getSyntaxDefinition().getLSPName() ) ) !=
|
||||
mLSPLanguagesDisabled.end() )
|
||||
return PluginRequestHandle::empty();
|
||||
|
||||
std::map<Int64, std::vector<LinterMatch>> matches;
|
||||
|
||||
@@ -275,6 +273,7 @@ void LinterPlugin::processNotification( const PluginManager::Notification& notif
|
||||
}
|
||||
|
||||
setMatches( doc, MatchOrigin::Diagnostics, matches );
|
||||
return PluginRequestHandle::empty();
|
||||
}
|
||||
|
||||
TextDocument* LinterPlugin::getDocumentFromURI( const URI& uri ) {
|
||||
@@ -286,8 +285,9 @@ TextDocument* LinterPlugin::getDocumentFromURI( const URI& uri ) {
|
||||
}
|
||||
|
||||
void LinterPlugin::load( const PluginManager* pluginManager ) {
|
||||
pluginManager->subscribeNotifications(
|
||||
this, [&]( const auto& notification ) { processNotification( notification ); } );
|
||||
pluginManager->subscribeMessages( this, [&]( const auto& notification ) -> PluginRequestHandle {
|
||||
return processMessage( notification );
|
||||
} );
|
||||
std::vector<std::string> paths;
|
||||
std::string path( pluginManager->getResourcesPath() + "plugins/linters.json" );
|
||||
if ( FileSystem::fileExists( path ) )
|
||||
|
||||
@@ -140,7 +140,7 @@ class LinterPlugin : public UICodeEditorPlugin {
|
||||
|
||||
size_t linterFilePatternPosition( const std::vector<std::string>& patterns );
|
||||
|
||||
void processNotification( const PluginManager::Notification& notification );
|
||||
PluginRequestHandle processMessage( const PluginMessage& notification );
|
||||
|
||||
TextDocument* getDocumentFromURI( const URI& uri );
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ LSPClientPlugin::LSPClientPlugin( const PluginManager* pluginManager ) :
|
||||
}
|
||||
|
||||
LSPClientPlugin::~LSPClientPlugin() {
|
||||
mManager->sendResponse( this, PluginMessageType::LSPClientPlugin,
|
||||
PluginMessageFormat::LSPClientPlugin, nullptr, -1 );
|
||||
|
||||
mClosing = true;
|
||||
Lock l( mDocMutex );
|
||||
for ( const auto& editor : mEditors ) {
|
||||
@@ -47,20 +50,62 @@ void LSPClientPlugin::update( UICodeEditor* ) {
|
||||
mClientManager.updateDirty();
|
||||
}
|
||||
|
||||
void LSPClientPlugin::processNotification( const PluginManager::Notification& notification ) {
|
||||
switch ( notification.type ) {
|
||||
case PluginManager::WorkspaceFolderChanged: {
|
||||
mClientManager.didChangeWorkspaceFolders( notification.asJSON()["folder"] );
|
||||
PluginRequestHandle LSPClientPlugin::processCodeCompletionRequest( const PluginMessage& msg ) {
|
||||
if ( !msg.isRequest() || !msg.isJSON() )
|
||||
return {};
|
||||
|
||||
const auto& data = msg.asJSON();
|
||||
if ( !data.contains( "uri" ) || !data.contains( "position" ) )
|
||||
return {};
|
||||
|
||||
TextPosition position( LSPConverter::fromJSON( data["position"] ) );
|
||||
if ( !position.isValid() )
|
||||
return {};
|
||||
|
||||
URI uri( data["uri"] );
|
||||
auto server = mClientManager.getOneLSPClientServer( uri );
|
||||
if ( !server )
|
||||
return {};
|
||||
|
||||
auto ret = server->documentCompletion(
|
||||
uri, position, [&]( const PluginIDType& id, const std::vector<LSPCompletionItem>& items ) {
|
||||
mManager->sendResponse( this, PluginMessageType::CodeCompletion,
|
||||
PluginMessageFormat::CodeCompletion, &items, id );
|
||||
} );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PluginRequestHandle LSPClientPlugin::processMessage( const PluginMessage& msg ) {
|
||||
switch ( msg.type ) {
|
||||
case PluginMessageType::WorkspaceFolderChanged: {
|
||||
mClientManager.didChangeWorkspaceFolders( msg.asJSON()["folder"] );
|
||||
break;
|
||||
}
|
||||
case PluginMessageType::CodeCompletion: {
|
||||
auto ret = processCodeCompletionRequest( msg );
|
||||
if ( !ret.isEmpty() )
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
case PluginMessageType::LSPClientPlugin: {
|
||||
if ( msg.isRequest() ) {
|
||||
mManager->sendResponse( this, PluginMessageType::LSPClientPlugin,
|
||||
PluginMessageFormat::LSPClientPlugin, this, -1 );
|
||||
return PluginRequestHandle::broadcast();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return PluginRequestHandle::empty();
|
||||
}
|
||||
|
||||
void LSPClientPlugin::load( const PluginManager* pluginManager ) {
|
||||
pluginManager->subscribeNotifications(
|
||||
this, [&]( const auto& notification ) { processNotification( notification ); } );
|
||||
pluginManager->subscribeMessages( this, [&]( const auto& notification ) -> PluginRequestHandle {
|
||||
return processMessage( notification );
|
||||
} );
|
||||
std::vector<std::string> paths;
|
||||
std::string path( pluginManager->getResourcesPath() + "plugins/lspclient.json" );
|
||||
if ( FileSystem::fileExists( path ) )
|
||||
@@ -92,8 +137,12 @@ void LSPClientPlugin::load( const PluginManager* pluginManager ) {
|
||||
if ( mDocs.find( doc.first ) != mDocs.end() )
|
||||
mClientManager.tryRunServer( doc.second );
|
||||
mDelayedDocs.clear();
|
||||
if ( mReady )
|
||||
if ( mReady ) {
|
||||
fireReadyCbs();
|
||||
|
||||
mManager->sendResponse( this, PluginMessageType::LSPClientPlugin,
|
||||
PluginMessageFormat::LSPClientPlugin, this, -1 );
|
||||
}
|
||||
}
|
||||
|
||||
void LSPClientPlugin::loadLSPConfig( std::vector<LSPDefinition>& lsps, const std::string& path ) {
|
||||
@@ -372,7 +421,7 @@ bool LSPClientPlugin::onMouseMove( UICodeEditor* editor, const Vector2i& positio
|
||||
return;
|
||||
server->documentHover(
|
||||
editor->getDocument().getURI(), currentMouseTextPosition( editor ),
|
||||
[&, editor, position]( const LSPHover& resp ) {
|
||||
[&, editor, position]( const Int64&, const LSPHover& resp ) {
|
||||
if ( resp.range.isValid() && !resp.contents.empty() ) {
|
||||
editor->runOnMainThread( [editor, resp, position, this]() {
|
||||
TextPosition startCursorPosition =
|
||||
@@ -425,4 +474,8 @@ void LSPClientPlugin::setHoverDelay( const Time& hoverDelay ) {
|
||||
mHoverDelay = hoverDelay;
|
||||
}
|
||||
|
||||
const LSPClientServerManager& LSPClientPlugin::getClientManager() const {
|
||||
return mClientManager;
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -64,6 +64,8 @@ class LSPClientPlugin : public UICodeEditorPlugin {
|
||||
|
||||
void setHoverDelay( const Time& hoverDelay );
|
||||
|
||||
const LSPClientServerManager& getClientManager() const;
|
||||
|
||||
protected:
|
||||
const PluginManager* mManager{ nullptr };
|
||||
std::shared_ptr<ThreadPool> mThreadPool;
|
||||
@@ -92,7 +94,9 @@ class LSPClientPlugin : public UICodeEditorPlugin {
|
||||
size_t lspFilePatternPosition( const std::vector<LSPDefinition>& lsps,
|
||||
const std::vector<std::string>& patterns );
|
||||
|
||||
void processNotification( const PluginManager::Notification& notification );
|
||||
PluginRequestHandle processMessage( const PluginMessage& msg );
|
||||
|
||||
PluginRequestHandle processCodeCompletionRequest( const PluginMessage& msg );
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -288,6 +288,7 @@ static void fromJson( LSPServerCapabilities& caps, const json& json ) {
|
||||
fromJson( caps.workspaceFolders, workspace["workspaceFolders"] );
|
||||
}
|
||||
caps.selectionRangeProvider = toBoolOrObject( json, "selectionRangeProvider" );
|
||||
caps.ready = true;
|
||||
}
|
||||
|
||||
static std::vector<LSPDiagnostic> parseDiagnosticsArr( const json& result ) {
|
||||
@@ -657,6 +658,54 @@ static std::vector<LSPCompletionItem> parseDocumentCompletion( const json& resul
|
||||
return ret;
|
||||
}
|
||||
|
||||
static LSPSignatureInformation parseSignatureInformation( const json& json ) {
|
||||
LSPSignatureInformation info;
|
||||
|
||||
info.label = json.value( MEMBER_LABEL, "" );
|
||||
info.documentation = parseMarkupContent( json.value( MEMBER_DOCUMENTATION, {} ) );
|
||||
const auto& params = json.at( "parameters" );
|
||||
for ( const auto& par : params ) {
|
||||
auto label = par.at( MEMBER_LABEL );
|
||||
int begin = -1, end = -1;
|
||||
if ( label.is_array() ) {
|
||||
auto& range = label;
|
||||
if ( range.size() == 2 ) {
|
||||
begin = range[0].get<int>();
|
||||
end = range[1].get<int>();
|
||||
if ( begin > (int)info.label.length() )
|
||||
begin = -1;
|
||||
if ( end > (int)info.label.length() )
|
||||
end = -1;
|
||||
}
|
||||
} else {
|
||||
auto sub = label.get<std::string>();
|
||||
if ( sub.length() ) {
|
||||
begin = info.label.find_first_of( sub );
|
||||
if ( begin >= 0 )
|
||||
end = begin + sub.length();
|
||||
}
|
||||
}
|
||||
info.parameters.push_back( { begin, end } );
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
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() );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::shared_ptr<LSPSelectionRange> parseSelectionRange( const json& selectionRange ) {
|
||||
auto current = std::make_shared<LSPSelectionRange>( LSPSelectionRange{} );
|
||||
std::shared_ptr<LSPSelectionRange> ret = current;
|
||||
@@ -729,7 +778,7 @@ void LSPClientServer::initialize() {
|
||||
|
||||
write(
|
||||
newRequest( "initialize", params ),
|
||||
[&]( const json& resp ) {
|
||||
[&]( const IdType&, const json& resp ) {
|
||||
#ifndef EE_DEBUG
|
||||
try {
|
||||
#endif
|
||||
@@ -746,7 +795,7 @@ void LSPClientServer::initialize() {
|
||||
write( newRequest( "initialized" ) );
|
||||
sendQueuedMessages();
|
||||
},
|
||||
[&]( const json& ) {} );
|
||||
[&]( const IdType&, const json& ) {} );
|
||||
}
|
||||
|
||||
LSPClientServer::LSPClientServer( LSPClientServerManager* manager, const String::HashType& id,
|
||||
@@ -795,18 +844,20 @@ const LSPServerCapabilities& LSPClientServer::getCapabilities() const {
|
||||
return mCapabilities;
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::cancel( int reqid ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::cancel( int reqid ) {
|
||||
Lock l( mHandlersMutex );
|
||||
if ( mHandlers.erase( reqid ) > 0 ) {
|
||||
auto params = json{ MEMBER_ID, reqid };
|
||||
return write( newRequest( "$/cancelRequest", params ) );
|
||||
}
|
||||
return RequestHandle();
|
||||
return LSPRequestHandle();
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::write( const json& msg, const JsonReplyHandler& h,
|
||||
const JsonReplyHandler& eh, const int id ) {
|
||||
RequestHandle ret;
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::write( const json& msg,
|
||||
const JsonReplyHandler& h,
|
||||
const JsonReplyHandler& eh,
|
||||
const int id ) {
|
||||
LSPRequestHandle ret;
|
||||
ret.server = this;
|
||||
|
||||
if ( !mProcess.isAlive() )
|
||||
@@ -818,7 +869,7 @@ LSPClientServer::RequestHandle LSPClientServer::write( const json& msg, const Js
|
||||
// notification == no handler
|
||||
if ( h ) {
|
||||
ob[MEMBER_ID] = ++mLastMsgId;
|
||||
ret.id = mLastMsgId;
|
||||
ret.mId = mLastMsgId;
|
||||
Lock l( mHandlersMutex );
|
||||
mHandlers[mLastMsgId] = { h, eh };
|
||||
} else if ( id ) {
|
||||
@@ -848,24 +899,24 @@ LSPClientServer::RequestHandle LSPClientServer::write( const json& msg, const Js
|
||||
return ret;
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::send( const json& msg, const JsonReplyHandler& h,
|
||||
const JsonReplyHandler& eh ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::send( const json& msg, const JsonReplyHandler& h,
|
||||
const JsonReplyHandler& eh ) {
|
||||
if ( mProcess.isAlive() ) {
|
||||
return write( msg, h, eh );
|
||||
} else {
|
||||
Log::debug( "LSPClientServer server %s Send for non-running server: %s", mLSP.name.c_str(),
|
||||
mLSP.name.c_str() );
|
||||
}
|
||||
return RequestHandle();
|
||||
return LSPRequestHandle();
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::didOpen( const URI& document,
|
||||
const std::string& text, int version ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::didOpen( const URI& document,
|
||||
const std::string& text, int version ) {
|
||||
auto params = textDocumentParams( textDocumentItem( document, mLSP.language, text, version ) );
|
||||
return send( newRequest( "textDocument/didOpen", params ) );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::didOpen( TextDocument* doc, int version ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::didOpen( TextDocument* doc, int version ) {
|
||||
if ( doc->isDirty() ) {
|
||||
IOStreamString text;
|
||||
doc->save( text, true );
|
||||
@@ -877,21 +928,21 @@ LSPClientServer::RequestHandle LSPClientServer::didOpen( TextDocument* doc, int
|
||||
}
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::didSave( const URI& document,
|
||||
const std::string& text ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::didSave( const URI& document,
|
||||
const std::string& text ) {
|
||||
auto params = textDocumentParams( document );
|
||||
if ( !text.empty() )
|
||||
params["text"] = text;
|
||||
return send( newRequest( "textDocument/didSave", params ) );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::didSave( TextDocument* doc ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::didSave( TextDocument* doc ) {
|
||||
return didSave( doc->getURI(), mCapabilities.textDocumentSync.save.includeText
|
||||
? doc->getText().toUtf8()
|
||||
: "" );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
LSPClientServer::LSPRequestHandle
|
||||
LSPClientServer::didChange( const URI& document, int version, const std::string& text,
|
||||
const std::vector<DocumentContentChange>& change ) {
|
||||
auto params = textDocumentParams( document, version );
|
||||
@@ -899,14 +950,14 @@ LSPClientServer::didChange( const URI& document, int version, const std::string&
|
||||
return send( newRequest( "textDocument/didChange", params ) );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
LSPClientServer::LSPRequestHandle
|
||||
LSPClientServer::didChange( TextDocument* doc, const std::vector<DocumentContentChange>& change ) {
|
||||
Lock l( mClientsMutex );
|
||||
auto it = mClients.find( doc );
|
||||
if ( it != mClients.end() )
|
||||
return didChange( doc->getURI(), it->second->getVersion(),
|
||||
change.empty() ? doc->getText().toUtf8() : "", change );
|
||||
return RequestHandle();
|
||||
return LSPRequestHandle();
|
||||
}
|
||||
|
||||
void LSPClientServer::updateDirty() {
|
||||
@@ -928,16 +979,24 @@ bool LSPClientServer::hasDocument( TextDocument* doc ) const {
|
||||
return std::find( mDocs.begin(), mDocs.end(), doc ) != mDocs.end();
|
||||
}
|
||||
|
||||
bool LSPClientServer::hasDocument( const URI& uri ) const {
|
||||
for ( const auto& doc : mDocs ) {
|
||||
if ( doc->getURI() == uri )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LSPClientServer::hasDocuments() const {
|
||||
return !mDocs.empty();
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::didClose( const URI& document ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::didClose( const URI& document ) {
|
||||
auto params = textDocumentParams( document );
|
||||
return send( newRequest( "textDocument/didClose", params ) );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::didClose( TextDocument* doc ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::didClose( TextDocument* doc ) {
|
||||
auto ret = didClose( doc->getURI() );
|
||||
Lock l( mClientsMutex );
|
||||
if ( mClients.erase( doc ) > 0 ) {
|
||||
@@ -956,41 +1015,41 @@ const std::shared_ptr<ThreadPool>& LSPClientServer::getThreadPool() const {
|
||||
return mManager->getThreadPool();
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentSymbols( const URI& document,
|
||||
const JsonReplyHandler& h,
|
||||
const JsonReplyHandler& eh ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::documentSymbols( const URI& document,
|
||||
const JsonReplyHandler& h,
|
||||
const JsonReplyHandler& eh ) {
|
||||
auto params = textDocumentParams( document );
|
||||
return send( newRequest( "textDocument/documentSymbol", params ), h, eh );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
LSPClientServer::LSPRequestHandle
|
||||
LSPClientServer::documentSymbols( const URI& document,
|
||||
const ReplyHandler<std::vector<LSPSymbolInformation>>& h,
|
||||
const ReplyHandler<LSPResponseError>& eh ) {
|
||||
return documentSymbols(
|
||||
document,
|
||||
[h]( const json& json ) {
|
||||
[h]( const IdType& id, const json& json ) {
|
||||
if ( h )
|
||||
h( parseDocumentSymbols( json ) );
|
||||
h( id, parseDocumentSymbols( json ) );
|
||||
},
|
||||
[eh]( const json& json ) {
|
||||
[eh]( const IdType& id, const json& json ) {
|
||||
if ( eh )
|
||||
eh( parseResponseError( json ) );
|
||||
eh( id, parseResponseError( json ) );
|
||||
} );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::workspaceSymbol( const std::string& querySymbol,
|
||||
const JsonReplyHandler& h ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::workspaceSymbol( const std::string& querySymbol,
|
||||
const JsonReplyHandler& h ) {
|
||||
auto params = json{ { MEMBER_QUERY, querySymbol } };
|
||||
return send( newRequest( "workspace/symbol", params ), h );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
LSPClientServer::LSPRequestHandle
|
||||
LSPClientServer::workspaceSymbol( const std::string& querySymbol,
|
||||
const SymbolInformationHandler& h ) {
|
||||
return workspaceSymbol( querySymbol, [h]( const json& json ) {
|
||||
return workspaceSymbol( querySymbol, [h]( const IdType& id, const json& json ) {
|
||||
if ( h )
|
||||
h( parseWorkspaceSymbols( json ) );
|
||||
h( id, parseWorkspaceSymbols( json ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -1031,9 +1090,9 @@ static json newError( const LSPErrorCode& code, const std::string& msg ) {
|
||||
void LSPClientServer::publishDiagnostics( const json& msg ) {
|
||||
LSPPublishDiagnosticsParams res = parsePublishDiagnostics( msg[MEMBER_PARAMS] );
|
||||
if ( mManager && mManager->getPluginManager() && mManager->getPlugin() ) {
|
||||
mManager->getPluginManager()->pushNotification( mManager->getPlugin(),
|
||||
PluginManager::PublishDiagnostics,
|
||||
PluginManager::Diagnostics, &res );
|
||||
mManager->getPluginManager()->sendBroadcast( mManager->getPlugin(),
|
||||
PluginMessageType::Diagnostics,
|
||||
PluginMessageFormat::Diagnostics, &res );
|
||||
}
|
||||
Log::debug( "LSPClientServer::publishDiagnostics: %s - returned %zu items",
|
||||
res.uri.toString().c_str(), res.diagnostics.size() );
|
||||
@@ -1158,9 +1217,9 @@ void LSPClientServer::readStdOut( const char* bytes, size_t n ) {
|
||||
auto& h = handler.second.first;
|
||||
auto& eh = handler.second.second;
|
||||
if ( res.contains( MEMBER_ERROR ) && eh ) {
|
||||
eh( res[MEMBER_ERROR] );
|
||||
eh( msgid, res[MEMBER_ERROR] );
|
||||
} else {
|
||||
h( res[MEMBER_RESULT] );
|
||||
h( msgid, res[MEMBER_RESULT] );
|
||||
}
|
||||
} else {
|
||||
Log::debug( "LSPClientServer::readStdOut server %s unexpected reply id: %d",
|
||||
@@ -1202,36 +1261,37 @@ void LSPClientServer::goToLocation( const json& res ) {
|
||||
mManager->goToLocation( locs.front() );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::getAndGoToLocation( const URI& document,
|
||||
const TextPosition& pos,
|
||||
const std::string& search ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::getAndGoToLocation( const URI& document,
|
||||
const TextPosition& pos,
|
||||
const std::string& search ) {
|
||||
auto params = textDocumentPositionParams( document, pos );
|
||||
return send( newRequest( search, params ), [this]( json res ) { goToLocation( res ); } );
|
||||
return send( newRequest( search, params ),
|
||||
[this]( const IdType&, const json& res ) { goToLocation( res ); } );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentDefinition( const URI& document,
|
||||
const TextPosition& pos ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::documentDefinition( const URI& document,
|
||||
const TextPosition& pos ) {
|
||||
return getAndGoToLocation( document, pos, "textDocument/definition" );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentDeclaration( const URI& document,
|
||||
const TextPosition& pos ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::documentDeclaration( const URI& document,
|
||||
const TextPosition& pos ) {
|
||||
return getAndGoToLocation( document, pos, "textDocument/declaration" );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentTypeDefinition( const URI& document,
|
||||
const TextPosition& pos ) {
|
||||
LSPClientServer::LSPRequestHandle
|
||||
LSPClientServer::documentTypeDefinition( const URI& document, const TextPosition& pos ) {
|
||||
return getAndGoToLocation( document, pos, "textDocument/typeDefinition" );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentImplementation( const URI& document,
|
||||
const TextPosition& pos ) {
|
||||
LSPClientServer::LSPRequestHandle
|
||||
LSPClientServer::documentImplementation( const URI& document, const TextPosition& pos ) {
|
||||
return getAndGoToLocation( document, pos, "textDocument/implementation" );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::switchSourceHeader( const URI& document ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::switchSourceHeader( const URI& document ) {
|
||||
return send( newRequest( "textDocument/switchSourceHeader", textDocumentURI( document ) ),
|
||||
[this]( json res ) {
|
||||
[this]( const IdType&, json res ) {
|
||||
if ( res.is_string() ) {
|
||||
mManager->goToLocation(
|
||||
{ res.get<std::string>(), TextRange{ { 0, 0 }, { 0, 0 } } } );
|
||||
@@ -1239,78 +1299,95 @@ LSPClientServer::RequestHandle LSPClientServer::switchSourceHeader( const URI& d
|
||||
} );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
LSPClientServer::LSPRequestHandle
|
||||
LSPClientServer::didChangeWorkspaceFolders( const std::vector<LSPWorkspaceFolder>& added,
|
||||
const std::vector<LSPWorkspaceFolder>& removed ) {
|
||||
auto params = changeWorkspaceFoldersParams( added, removed );
|
||||
return send( newRequest( "workspace/didChangeWorkspaceFolders", params ) );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentCodeAction(
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::documentCodeAction(
|
||||
const URI& document, const TextRange& range, const std::vector<std::string>& kinds,
|
||||
std::vector<LSPDiagnostic> diagnostics, const JsonReplyHandler& h ) {
|
||||
auto params = codeActionParams( document, range, kinds, std::move( diagnostics ) );
|
||||
return send( newRequest( "textDocument/codeAction", params ), h );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentCodeAction(
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::documentCodeAction(
|
||||
const URI& document, const TextRange& range, const std::vector<std::string>& kinds,
|
||||
std::vector<LSPDiagnostic> diagnostics, const CodeActionHandler& h ) {
|
||||
return documentCodeAction( document, range, kinds, diagnostics, [h]( const json& json ) {
|
||||
if ( h )
|
||||
h( parseCodeAction( json ) );
|
||||
} );
|
||||
return documentCodeAction( document, range, kinds, diagnostics,
|
||||
[h]( const IdType& id, const json& json ) {
|
||||
if ( h )
|
||||
h( id, parseCodeAction( json ) );
|
||||
} );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentHover( const URI& document,
|
||||
const TextPosition& pos,
|
||||
const JsonReplyHandler& h ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::documentHover( const URI& document,
|
||||
const TextPosition& pos,
|
||||
const JsonReplyHandler& h ) {
|
||||
auto params = textDocumentPositionParams( document, pos );
|
||||
return send( newRequest( "textDocument/hover", params ), h );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentHover( const URI& document,
|
||||
const TextPosition& pos,
|
||||
const HoverHandler& h ) {
|
||||
return documentHover( document, pos, [h]( const json& json ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::documentHover( const URI& document,
|
||||
const TextPosition& pos,
|
||||
const HoverHandler& h ) {
|
||||
return documentHover( document, pos, [h]( const IdType& id, const json& json ) {
|
||||
if ( h )
|
||||
h( parseHover( json ) );
|
||||
h( id, parseHover( json ) );
|
||||
} );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentCompletion( const URI& document,
|
||||
const TextPosition& pos,
|
||||
const JsonReplyHandler& h ) {
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::documentCompletion( const URI& document,
|
||||
const TextPosition& pos,
|
||||
const JsonReplyHandler& h ) {
|
||||
auto params = textDocumentPositionParams( document, pos );
|
||||
return send( newRequest( "textDocument/completion", params ), h );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentCompletion( const URI& document,
|
||||
const TextPosition& pos,
|
||||
const CompletionHandler& h ) {
|
||||
return documentCompletion( document, pos, [h]( const json& json ) {
|
||||
LSPClientServer::LSPRequestHandle
|
||||
LSPClientServer::documentCompletion( const URI& document, const TextPosition& pos,
|
||||
const CompletionHandler& h ) {
|
||||
return documentCompletion( document, pos, [h]( const IdType& id, const json& json ) {
|
||||
if ( h )
|
||||
h( parseDocumentCompletion( json ) );
|
||||
h( id, parseDocumentCompletion( json ) );
|
||||
} );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::signatureHelp( const URI& document,
|
||||
const TextPosition& pos,
|
||||
const JsonReplyHandler& h ) {
|
||||
auto params = textDocumentPositionParams( document, pos );
|
||||
return send( newRequest( "textDocument/signatureHelp", params ), h );
|
||||
}
|
||||
|
||||
LSPClientServer::LSPRequestHandle LSPClientServer::signatureHelp( const URI& document,
|
||||
const TextPosition& pos,
|
||||
const SignatureHelpHandler& h ) {
|
||||
return signatureHelp( document, pos, [h]( const IdType& id, const json& json ) {
|
||||
if ( h )
|
||||
h( id, parseSignatureHelp( json ) );
|
||||
} );
|
||||
}
|
||||
|
||||
LSPClientServer::LSPRequestHandle
|
||||
LSPClientServer::selectionRange( const URI& document, const std::vector<TextPosition>& positions,
|
||||
const JsonReplyHandler& h ) {
|
||||
auto params = textDocumentPositionsParams( document, positions );
|
||||
return send( newRequest( "textDocument/selectionRange", params ), h );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
LSPClientServer::LSPRequestHandle
|
||||
LSPClientServer::selectionRange( const URI& document, const std::vector<TextPosition>& positions,
|
||||
const SelectionRangeHandler& h ) {
|
||||
return selectionRange( document, positions, [h]( const json& json ) {
|
||||
return selectionRange( document, positions, [h]( const IdType& id, const json& json ) {
|
||||
if ( h )
|
||||
h( parseSelectionRanges( json ) );
|
||||
h( id, parseSelectionRanges( json ) );
|
||||
} );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
LSPClientServer::LSPRequestHandle
|
||||
LSPClientServer::documentSemanticTokensFull( const URI& document, bool delta,
|
||||
const std::string& requestId, const TextRange& range,
|
||||
const JsonReplyHandler& h ) {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#ifndef ECODE_LSPCLIENTSERVER_HPP
|
||||
#define ECODE_LSPCLIENTSERVER_HPP
|
||||
|
||||
#include "lspprotocol.hpp"
|
||||
#include "../pluginmanager.hpp"
|
||||
#include "lspdefinition.hpp"
|
||||
#include "lspdocumentclient.hpp"
|
||||
#include "lspprotocol.hpp"
|
||||
#include <eepp/system/process.hpp>
|
||||
#include <eepp/ui/doc/textdocument.hpp>
|
||||
#include <eepp/ui/doc/undostack.hpp>
|
||||
@@ -25,7 +26,8 @@ class LSPClientServerManager;
|
||||
|
||||
class LSPClientServer {
|
||||
public:
|
||||
template <typename T> using ReplyHandler = std::function<void( const T& )>;
|
||||
using IdType = PluginIDType;
|
||||
template <typename T> using ReplyHandler = std::function<void( const IdType& id, const T& )>;
|
||||
|
||||
using JsonReplyHandler = ReplyHandler<json>;
|
||||
using CodeActionHandler = ReplyHandler<std::vector<LSPCodeAction>>;
|
||||
@@ -33,19 +35,18 @@ class LSPClientServer {
|
||||
using CompletionHandler = ReplyHandler<std::vector<LSPCompletionItem>>;
|
||||
using SymbolInformationHandler = ReplyHandler<std::vector<LSPSymbolInformation>>;
|
||||
using SelectionRangeHandler = ReplyHandler<std::vector<std::shared_ptr<LSPSelectionRange>>>;
|
||||
using SignatureHelpHandler = ReplyHandler<LSPSignatureHelp>;
|
||||
|
||||
class LSPRequestHandle : public PluginRequestHandle {
|
||||
public:
|
||||
void cancel() {
|
||||
if ( server && mId != 0 )
|
||||
server->cancel( mId );
|
||||
}
|
||||
|
||||
class RequestHandle {
|
||||
private:
|
||||
friend class LSPClientServer;
|
||||
LSPClientServer* server;
|
||||
int id = 0;
|
||||
|
||||
public:
|
||||
RequestHandle& cancel() {
|
||||
if ( server )
|
||||
server->cancel( id );
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
LSPClientServer( LSPClientServerManager* manager, const String::HashType& id,
|
||||
@@ -63,100 +64,111 @@ class LSPClientServer {
|
||||
|
||||
const std::shared_ptr<ThreadPool>& getThreadPool() const;
|
||||
|
||||
RequestHandle cancel( int id );
|
||||
LSPRequestHandle cancel( int id );
|
||||
|
||||
RequestHandle send( const json& msg, const JsonReplyHandler& h = nullptr,
|
||||
const JsonReplyHandler& eh = nullptr );
|
||||
LSPRequestHandle send( const json& msg, const JsonReplyHandler& h = nullptr,
|
||||
const JsonReplyHandler& eh = nullptr );
|
||||
|
||||
const LSPDefinition& getDefinition() const { return mLSP; }
|
||||
|
||||
RequestHandle documentSymbols( const URI& document, const JsonReplyHandler& h,
|
||||
const JsonReplyHandler& eh );
|
||||
LSPRequestHandle documentSymbols( const URI& document, const JsonReplyHandler& h,
|
||||
const JsonReplyHandler& eh );
|
||||
|
||||
RequestHandle documentSymbols( const URI& document,
|
||||
const ReplyHandler<std::vector<LSPSymbolInformation>>& h,
|
||||
const ReplyHandler<LSPResponseError>& eh );
|
||||
LSPRequestHandle documentSymbols( const URI& document,
|
||||
const ReplyHandler<std::vector<LSPSymbolInformation>>& h,
|
||||
const ReplyHandler<LSPResponseError>& eh );
|
||||
|
||||
RequestHandle didOpen( const URI& document, const std::string& text, int version );
|
||||
LSPRequestHandle didOpen( const URI& document, const std::string& text, int version );
|
||||
|
||||
RequestHandle didOpen( TextDocument* doc, int version );
|
||||
LSPRequestHandle didOpen( TextDocument* doc, int version );
|
||||
|
||||
RequestHandle didSave( const URI& document, const std::string& text );
|
||||
LSPRequestHandle didSave( const URI& document, const std::string& text );
|
||||
|
||||
RequestHandle didSave( TextDocument* doc );
|
||||
LSPRequestHandle didSave( TextDocument* doc );
|
||||
|
||||
RequestHandle didClose( const URI& document );
|
||||
LSPRequestHandle didClose( const URI& document );
|
||||
|
||||
RequestHandle didClose( TextDocument* document );
|
||||
LSPRequestHandle didClose( TextDocument* document );
|
||||
|
||||
RequestHandle didChange( const URI& document, int version, const std::string& text,
|
||||
const std::vector<DocumentContentChange>& change = {} );
|
||||
LSPRequestHandle didChange( const URI& document, int version, const std::string& text,
|
||||
const std::vector<DocumentContentChange>& change = {} );
|
||||
|
||||
RequestHandle didChange( TextDocument* doc,
|
||||
const std::vector<DocumentContentChange>& change = {} );
|
||||
LSPRequestHandle didChange( TextDocument* doc,
|
||||
const std::vector<DocumentContentChange>& change = {} );
|
||||
|
||||
RequestHandle documentDefinition( const URI& document, const TextPosition& pos );
|
||||
LSPRequestHandle documentDefinition( const URI& document, const TextPosition& pos );
|
||||
|
||||
RequestHandle documentDeclaration( const URI& document, const TextPosition& pos );
|
||||
LSPRequestHandle documentDeclaration( const URI& document, const TextPosition& pos );
|
||||
|
||||
RequestHandle documentTypeDefinition( const URI& document, const TextPosition& pos );
|
||||
LSPRequestHandle documentTypeDefinition( const URI& document, const TextPosition& pos );
|
||||
|
||||
RequestHandle documentImplementation( const URI& document, const TextPosition& pos );
|
||||
LSPRequestHandle documentImplementation( const URI& document, const TextPosition& pos );
|
||||
|
||||
void updateDirty();
|
||||
|
||||
bool hasDocument( TextDocument* doc ) const;
|
||||
|
||||
bool hasDocument( const URI& uri ) const;
|
||||
|
||||
bool hasDocuments() const;
|
||||
|
||||
RequestHandle didChangeWorkspaceFolders( const std::vector<LSPWorkspaceFolder>& added,
|
||||
const std::vector<LSPWorkspaceFolder>& removed );
|
||||
LSPRequestHandle didChangeWorkspaceFolders( const std::vector<LSPWorkspaceFolder>& added,
|
||||
const std::vector<LSPWorkspaceFolder>& removed );
|
||||
|
||||
void publishDiagnostics( const json& msg );
|
||||
|
||||
void workDoneProgress( const LSPWorkDoneProgressParams& workDoneParams );
|
||||
|
||||
RequestHandle getAndGoToLocation( const URI& document, const TextPosition& pos,
|
||||
const std::string& search );
|
||||
LSPRequestHandle getAndGoToLocation( const URI& document, const TextPosition& pos,
|
||||
const std::string& search );
|
||||
|
||||
RequestHandle switchSourceHeader( const URI& document );
|
||||
LSPRequestHandle switchSourceHeader( const URI& document );
|
||||
|
||||
RequestHandle documentCodeAction( const URI& document, const TextRange& range,
|
||||
const std::vector<std::string>& kinds,
|
||||
std::vector<LSPDiagnostic> diagnostics,
|
||||
const JsonReplyHandler& h );
|
||||
LSPRequestHandle documentCodeAction( const URI& document, const TextRange& range,
|
||||
const std::vector<std::string>& kinds,
|
||||
std::vector<LSPDiagnostic> diagnostics,
|
||||
const JsonReplyHandler& h );
|
||||
|
||||
RequestHandle documentCodeAction( const URI& document, const TextRange& range,
|
||||
const std::vector<std::string>& kinds,
|
||||
std::vector<LSPDiagnostic> diagnostics,
|
||||
const CodeActionHandler& h );
|
||||
LSPRequestHandle documentCodeAction( const URI& document, const TextRange& range,
|
||||
const std::vector<std::string>& kinds,
|
||||
std::vector<LSPDiagnostic> diagnostics,
|
||||
const CodeActionHandler& h );
|
||||
|
||||
RequestHandle documentHover( const URI& document, const TextPosition& pos,
|
||||
const JsonReplyHandler& h );
|
||||
LSPRequestHandle documentHover( const URI& document, const TextPosition& pos,
|
||||
const JsonReplyHandler& h );
|
||||
|
||||
RequestHandle documentHover( const URI& document, const TextPosition& pos,
|
||||
const HoverHandler& h );
|
||||
LSPRequestHandle documentHover( const URI& document, const TextPosition& pos,
|
||||
const HoverHandler& h );
|
||||
|
||||
RequestHandle documentCompletion( const URI& document, const TextPosition& pos,
|
||||
const JsonReplyHandler& h );
|
||||
LSPRequestHandle documentCompletion( const URI& document, const TextPosition& pos,
|
||||
const JsonReplyHandler& h );
|
||||
|
||||
RequestHandle documentCompletion( const URI& document, const TextPosition& pos,
|
||||
const CompletionHandler& h );
|
||||
LSPRequestHandle documentCompletion( const URI& document, const TextPosition& pos,
|
||||
const CompletionHandler& h );
|
||||
|
||||
RequestHandle workspaceSymbol( const std::string& querySymbol, const JsonReplyHandler& h );
|
||||
LSPRequestHandle workspaceSymbol( const std::string& querySymbol, const JsonReplyHandler& h );
|
||||
|
||||
RequestHandle workspaceSymbol( const std::string& querySymbol,
|
||||
const SymbolInformationHandler& h );
|
||||
LSPRequestHandle workspaceSymbol( const std::string& querySymbol,
|
||||
const SymbolInformationHandler& h );
|
||||
|
||||
RequestHandle selectionRange( const URI& document, const std::vector<TextPosition>& positions,
|
||||
const JsonReplyHandler& h );
|
||||
LSPRequestHandle selectionRange( const URI& document,
|
||||
const std::vector<TextPosition>& positions,
|
||||
const JsonReplyHandler& h );
|
||||
|
||||
RequestHandle selectionRange( const URI& document, const std::vector<TextPosition>& positions,
|
||||
const SelectionRangeHandler& h );
|
||||
LSPRequestHandle selectionRange( const URI& document,
|
||||
const std::vector<TextPosition>& positions,
|
||||
const SelectionRangeHandler& h );
|
||||
|
||||
RequestHandle documentSemanticTokensFull( const URI& document, bool delta,
|
||||
const std::string& requestId, const TextRange& range,
|
||||
const JsonReplyHandler& h );
|
||||
LSPRequestHandle documentSemanticTokensFull( const URI& document, bool delta,
|
||||
const std::string& requestId,
|
||||
const TextRange& range,
|
||||
const JsonReplyHandler& h );
|
||||
|
||||
LSPRequestHandle signatureHelp( const URI& document, const TextPosition& pos,
|
||||
const JsonReplyHandler& h );
|
||||
|
||||
LSPRequestHandle signatureHelp( const URI& document, const TextPosition& pos,
|
||||
const SignatureHelpHandler& h );
|
||||
|
||||
protected:
|
||||
LSPClientServerManager* mManager{ nullptr };
|
||||
@@ -186,8 +198,8 @@ class LSPClientServer {
|
||||
|
||||
void readStdErr( const char* bytes, size_t n );
|
||||
|
||||
RequestHandle write( const json& msg, const JsonReplyHandler& h = nullptr,
|
||||
const JsonReplyHandler& eh = nullptr, const int id = 0 );
|
||||
LSPRequestHandle write( const json& msg, const JsonReplyHandler& h = nullptr,
|
||||
const JsonReplyHandler& eh = nullptr, const int id = 0 );
|
||||
|
||||
void initialize();
|
||||
|
||||
|
||||
@@ -203,6 +203,16 @@ LSPClientServerManager::getLSPClientServers( const std::shared_ptr<TextDocument>
|
||||
return servers;
|
||||
}
|
||||
|
||||
std::vector<LSPClientServer*> LSPClientServerManager::getLSPClientServers( const URI& uri ) {
|
||||
std::vector<LSPClientServer*> servers;
|
||||
Lock l( mClientsMutex );
|
||||
for ( auto& server : mClients ) {
|
||||
if ( server.second->hasDocument( uri ) )
|
||||
servers.push_back( server.second.get() );
|
||||
}
|
||||
return servers;
|
||||
}
|
||||
|
||||
LSPClientServer* LSPClientServerManager::getOneLSPClientServer( UICodeEditor* editor ) {
|
||||
return getOneLSPClientServer( editor->getDocumentRef() );
|
||||
}
|
||||
@@ -217,4 +227,13 @@ LSPClientServerManager::getOneLSPClientServer( const std::shared_ptr<TextDocumen
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LSPClientServer* LSPClientServerManager::getOneLSPClientServer( const URI& uri ) {
|
||||
Lock l( mClientsMutex );
|
||||
for ( auto& server : mClients ) {
|
||||
if ( server.second->hasDocument( uri ) )
|
||||
return server.second.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -41,10 +41,14 @@ class LSPClientServerManager {
|
||||
|
||||
std::vector<LSPClientServer*> getLSPClientServers( const std::shared_ptr<TextDocument>& doc );
|
||||
|
||||
std::vector<LSPClientServer*> getLSPClientServers( const URI& uri );
|
||||
|
||||
LSPClientServer* getOneLSPClientServer( UICodeEditor* editor );
|
||||
|
||||
LSPClientServer* getOneLSPClientServer( const std::shared_ptr<TextDocument>& doc );
|
||||
|
||||
LSPClientServer* getOneLSPClientServer( const URI& uri );
|
||||
|
||||
void getAndGoToLocation( const std::shared_ptr<TextDocument>& doc, const std::string& search );
|
||||
|
||||
const PluginManager* getPluginManager() const;
|
||||
|
||||
@@ -80,6 +80,7 @@ struct LSPWorkspaceFoldersServerCapabilities {
|
||||
};
|
||||
|
||||
struct LSPServerCapabilities {
|
||||
bool ready = false;
|
||||
LSPTextDocumentSyncOptions textDocumentSync;
|
||||
bool hoverProvider = false;
|
||||
LSPCompletionOptions completionProvider;
|
||||
@@ -302,6 +303,33 @@ struct LSPSelectionRange {
|
||||
std::shared_ptr<LSPSelectionRange> parent;
|
||||
};
|
||||
|
||||
struct LSPParameterInformation {
|
||||
// offsets into overall signature label
|
||||
// (-1 if invalid)
|
||||
int start;
|
||||
int end;
|
||||
};
|
||||
|
||||
struct LSPSignatureInformation {
|
||||
std::string label;
|
||||
LSPMarkupContent documentation;
|
||||
std::vector<LSPParameterInformation> parameters;
|
||||
};
|
||||
|
||||
struct LSPSignatureHelp {
|
||||
std::vector<LSPSignatureInformation> signatures;
|
||||
int activeSignature;
|
||||
int activeParameter;
|
||||
};
|
||||
|
||||
struct LSPConverter {
|
||||
static TextPosition fromJSON( const nlohmann::json& data ) {
|
||||
if ( data.contains( "line" ) && data.contains( "character" ) )
|
||||
return { data["line"].get<Int64>(), data["character"].get<Int64>() };
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
#endif // ECODE_LSPCLIENTPROTOCOL_HPP
|
||||
|
||||
@@ -13,6 +13,7 @@ PluginManager::PluginManager( const std::string& resourcesPath, const std::strin
|
||||
mResourcesPath( resourcesPath ), mPluginsPath( pluginsPath ), mThreadPool( pool ) {}
|
||||
|
||||
PluginManager::~PluginManager() {
|
||||
mClosing = true;
|
||||
for ( auto& plugin : mPlugins )
|
||||
eeDelete( plugin.second );
|
||||
}
|
||||
@@ -105,26 +106,54 @@ const std::string& PluginManager::getWorkspaceFolder() const {
|
||||
void PluginManager::setWorkspaceFolder( const std::string& workspaceFolder ) {
|
||||
mWorkspaceFolder = workspaceFolder;
|
||||
json data{ { "folder", mWorkspaceFolder } };
|
||||
sendNotification( NotificationType::WorkspaceFolderChanged, NotificationFormat::JSON, &data );
|
||||
sendBroadcast( PluginMessageType::WorkspaceFolderChanged, PluginMessageFormat::JSON, &data );
|
||||
}
|
||||
|
||||
void PluginManager::pushNotification( UICodeEditorPlugin* pluginWho, NotificationType notification,
|
||||
NotificationFormat format, void* data ) const {
|
||||
PluginRequestHandle PluginManager::sendRequest( UICodeEditorPlugin* pluginWho,
|
||||
PluginMessageType type, PluginMessageFormat format,
|
||||
const void* data ) const {
|
||||
if ( mClosing )
|
||||
return PluginRequestHandle::empty();
|
||||
for ( const auto& plugin : mSubscribedPlugins ) {
|
||||
if ( pluginWho->getId() != plugin.first ) {
|
||||
auto handle = plugin.second( { type, format, data } );
|
||||
if ( !handle.isEmpty() )
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
return PluginRequestHandle::empty();
|
||||
}
|
||||
|
||||
void PluginManager::sendResponse( UICodeEditorPlugin* pluginWho, PluginMessageType type,
|
||||
PluginMessageFormat format, const void* data,
|
||||
const PluginIDType& responseID ) const {
|
||||
if ( mClosing )
|
||||
return;
|
||||
for ( const auto& plugin : mSubscribedPlugins )
|
||||
if ( pluginWho->getId() != plugin.first )
|
||||
plugin.second( { notification, format, data } );
|
||||
plugin.second( { type, format, data, responseID } );
|
||||
}
|
||||
|
||||
void PluginManager::subscribeNotifications( UICodeEditorPlugin* plugin,
|
||||
std::function<void( const Notification& )> cb ) const {
|
||||
void PluginManager::sendBroadcast( UICodeEditorPlugin* pluginWho, PluginMessageType type,
|
||||
PluginMessageFormat format, void* data ) const {
|
||||
if ( mClosing )
|
||||
return;
|
||||
for ( const auto& plugin : mSubscribedPlugins )
|
||||
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;
|
||||
if ( !mWorkspaceFolder.empty() ) {
|
||||
json data{ { "folder", mWorkspaceFolder } };
|
||||
cb( { NotificationType::WorkspaceFolderChanged, NotificationFormat::JSON, &data } );
|
||||
cb( { PluginMessageType::WorkspaceFolderChanged, PluginMessageFormat::JSON, &data } );
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::unsubscribeNotifications( UICodeEditorPlugin* plugin ) const {
|
||||
void PluginManager::unsubscribeMessages( UICodeEditorPlugin* plugin ) const {
|
||||
const_cast<PluginManager*>( this )->mSubscribedPlugins.erase( plugin->getId() );
|
||||
}
|
||||
|
||||
@@ -132,8 +161,10 @@ void PluginManager::setSplitter( UICodeEditorSplitter* splitter ) {
|
||||
mSplitter = splitter;
|
||||
}
|
||||
|
||||
void PluginManager::sendNotification( const NotificationType& notification,
|
||||
const NotificationFormat& format, void* data ) {
|
||||
void PluginManager::sendBroadcast( const PluginMessageType& notification,
|
||||
const PluginMessageFormat& format, void* data ) {
|
||||
if ( mClosing )
|
||||
return;
|
||||
for ( const auto& plugin : mSubscribedPlugins )
|
||||
plugin.second( { notification, format, data } );
|
||||
}
|
||||
|
||||
@@ -56,22 +56,71 @@ struct PluginDefinition {
|
||||
PluginVersion version;
|
||||
};
|
||||
|
||||
enum class PluginMessageType {
|
||||
WorkspaceFolderChanged, // Broadcast the workspace folder from the application to the plugins
|
||||
Diagnostics, // Broadcast a document diagnostics from the LSP Client
|
||||
CodeCompletion, // Request the LSP Client to start a code completion in the requested document
|
||||
// and position
|
||||
LSPClientPlugin, // Request and/or Broadcast the LSP Client
|
||||
Undefined
|
||||
};
|
||||
|
||||
enum class PluginMessageFormat { JSON, Diagnostics, CodeCompletion, LSPClientPlugin };
|
||||
|
||||
using PluginIDType = Int64;
|
||||
|
||||
class LSPClientPlugin;
|
||||
|
||||
struct PluginMessage {
|
||||
PluginMessageType type;
|
||||
PluginMessageFormat format;
|
||||
const void* data;
|
||||
PluginIDType responseID{ 0 }; // 0 if it's not a response;
|
||||
|
||||
const nlohmann::json& asJSON() const { return *static_cast<const nlohmann::json*>( data ); }
|
||||
|
||||
bool isJSON() const { return format == PluginMessageFormat::JSON; }
|
||||
|
||||
const LSPPublishDiagnosticsParams& asDiagnostics() const {
|
||||
return *static_cast<const LSPPublishDiagnosticsParams*>( data );
|
||||
}
|
||||
|
||||
const std::vector<LSPCompletionItem>& asCodeCompletion() const {
|
||||
return *static_cast<const std::vector<LSPCompletionItem>*>( data );
|
||||
}
|
||||
|
||||
const LSPClientPlugin* asLSPClientPlugin() const {
|
||||
return static_cast<const LSPClientPlugin*>( data );
|
||||
}
|
||||
|
||||
bool isResponse() const { return -1 != responseID && 0 != responseID; }
|
||||
|
||||
bool isRequest() const { return -1 != responseID && 0 == responseID; }
|
||||
|
||||
bool isBroadcast() const { return -1 == responseID; }
|
||||
};
|
||||
|
||||
class PluginRequestHandle {
|
||||
public:
|
||||
static PluginRequestHandle broadcast() { return PluginRequestHandle( -1 ); }
|
||||
|
||||
static PluginRequestHandle empty() { return PluginRequestHandle(); }
|
||||
|
||||
PluginRequestHandle() {}
|
||||
PluginRequestHandle( int id ) : mId( id ) {}
|
||||
virtual PluginIDType id() const { return mId; }
|
||||
virtual void cancel() {}
|
||||
|
||||
bool isEmpty() const { return mId == 0; }
|
||||
|
||||
bool isBroadcast() const { return mId == -1; }
|
||||
|
||||
protected:
|
||||
PluginIDType mId{ 0 };
|
||||
};
|
||||
|
||||
class PluginManager {
|
||||
public:
|
||||
enum NotificationType { WorkspaceFolderChanged, PublishDiagnostics };
|
||||
enum NotificationFormat { JSON, Diagnostics };
|
||||
struct Notification {
|
||||
NotificationType type;
|
||||
NotificationFormat format;
|
||||
void* data;
|
||||
|
||||
nlohmann::json& asJSON() const { return *static_cast<nlohmann::json*>( data ); }
|
||||
|
||||
LSPPublishDiagnosticsParams& asDiagnostics() const {
|
||||
return *static_cast<LSPPublishDiagnosticsParams*>( data );
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr int versionNumber( int major, int minor, int patch ) {
|
||||
return ( (major)*1000 + (minor)*100 + ( patch ) );
|
||||
}
|
||||
@@ -117,13 +166,23 @@ class PluginManager {
|
||||
|
||||
void setWorkspaceFolder( const std::string& workspaceFolder );
|
||||
|
||||
void pushNotification( UICodeEditorPlugin* pluginWho, NotificationType, NotificationFormat,
|
||||
void* data ) const;
|
||||
PluginRequestHandle sendRequest( UICodeEditorPlugin* pluginWho, PluginMessageType type,
|
||||
PluginMessageFormat format, const void* data ) const;
|
||||
|
||||
void subscribeNotifications( UICodeEditorPlugin* plugin,
|
||||
std::function<void( const Notification& )> cb ) const;
|
||||
void sendResponse( UICodeEditorPlugin* pluginWho, PluginMessageType type,
|
||||
PluginMessageFormat format, const void* data,
|
||||
const PluginIDType& responseID ) const;
|
||||
|
||||
void unsubscribeNotifications( UICodeEditorPlugin* plugin ) const;
|
||||
void sendBroadcast( UICodeEditorPlugin* pluginWho, PluginMessageType, PluginMessageFormat,
|
||||
void* data ) const;
|
||||
|
||||
void sendBroadcast( const PluginMessageType& notification, const PluginMessageFormat& format,
|
||||
void* data );
|
||||
|
||||
void subscribeMessages( UICodeEditorPlugin* plugin,
|
||||
std::function<PluginRequestHandle( const PluginMessage& )> cb ) const;
|
||||
|
||||
void unsubscribeMessages( UICodeEditorPlugin* plugin ) const;
|
||||
|
||||
protected:
|
||||
friend class App;
|
||||
@@ -135,14 +194,16 @@ class PluginManager {
|
||||
std::map<std::string, PluginDefinition> mDefinitions;
|
||||
std::shared_ptr<ThreadPool> mThreadPool;
|
||||
UICodeEditorSplitter* mSplitter{ nullptr };
|
||||
std::map<std::string, std::function<void( const Notification& )>> mSubscribedPlugins;
|
||||
std::map<std::string, std::function<PluginRequestHandle( const PluginMessage& )>>
|
||||
mSubscribedPlugins;
|
||||
bool mClosing{ false };
|
||||
|
||||
bool hasDefinition( const std::string& id );
|
||||
|
||||
void setSplitter( UICodeEditorSplitter* splitter );
|
||||
|
||||
void sendNotification( const NotificationType& notification, const NotificationFormat& format,
|
||||
void* data );
|
||||
PluginRequestHandle sendRequest( const PluginMessageType& notification,
|
||||
const PluginMessageFormat& format, void* data );
|
||||
};
|
||||
|
||||
class PluginsModel : public Model {
|
||||
|
||||
Reference in New Issue
Block a user