mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-06-01 11:06:30 +03:00
More LSP work.
This commit is contained in:
@@ -26,6 +26,10 @@ LSPClientPlugin::~LSPClientPlugin() {
|
||||
}
|
||||
}
|
||||
|
||||
void LSPClientPlugin::update( UICodeEditor* ) {
|
||||
mClientManager.updateDirty();
|
||||
}
|
||||
|
||||
void LSPClientPlugin::load( const PluginManager* pluginManager ) {
|
||||
std::vector<std::string> paths;
|
||||
std::string path( pluginManager->getResourcesPath() + "plugins/lspclient.json" );
|
||||
|
||||
@@ -33,6 +33,8 @@ class LSPClientPlugin : public UICodeEditorPlugin {
|
||||
|
||||
virtual ~LSPClientPlugin();
|
||||
|
||||
virtual void update( UICodeEditor* );
|
||||
|
||||
std::string getId() { return Definition().id; }
|
||||
|
||||
std::string getTitle() { return Definition().name; }
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
#include "lspclientserver.hpp"
|
||||
#include "lspclientservermanager.hpp"
|
||||
#include <algorithm>
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/iostreamstring.hpp>
|
||||
#include <eepp/system/lock.hpp>
|
||||
#include <eepp/system/log.hpp>
|
||||
#include <eepp/system/sys.hpp>
|
||||
#include <eepp/ui/doc/textdocument.hpp>
|
||||
@@ -69,13 +74,14 @@ static json textDocumentParams( const URI& document, int version = -1 ) {
|
||||
return textDocumentParams( versionedTextDocumentIdentifier( document, version ) );
|
||||
}
|
||||
|
||||
LSPClientServer::LSPClientServer( const LSPDefinition& lsp, const std::string& rootPath ) :
|
||||
mLSP( lsp ), mRootPath( rootPath ) {}
|
||||
LSPClientServer::LSPClientServer( LSPClientServerManager* manager, const String::HashType& id,
|
||||
const LSPDefinition& lsp, const std::string& rootPath ) :
|
||||
mManager( manager ), mId( id ), mLSP( lsp ), mRootPath( rootPath ) {}
|
||||
|
||||
LSPClientServer::~LSPClientServer() {
|
||||
for ( const auto& client : mClients ) {
|
||||
Lock l( mClientsMutex );
|
||||
for ( const auto& client : mClients )
|
||||
client.first->unregisterClient( client.second.get() );
|
||||
}
|
||||
}
|
||||
|
||||
bool LSPClientServer::start() {
|
||||
@@ -91,8 +97,9 @@ bool LSPClientServer::start() {
|
||||
}
|
||||
|
||||
bool LSPClientServer::registerDoc( const std::shared_ptr<TextDocument>& doc ) {
|
||||
for ( auto& cdoc : mDocs ) {
|
||||
if ( cdoc.get() == doc.get() ) {
|
||||
Lock l( mClientsMutex );
|
||||
for ( TextDocument* cdoc : mDocs ) {
|
||||
if ( cdoc == doc.get() ) {
|
||||
if ( mClients.find( doc.get() ) == mClients.end() ) {
|
||||
mClients[doc.get()] = std::make_unique<LSPDocumentClient>( this, doc.get() );
|
||||
return true;
|
||||
@@ -102,11 +109,19 @@ bool LSPClientServer::registerDoc( const std::shared_ptr<TextDocument>& doc ) {
|
||||
}
|
||||
|
||||
mClients[doc.get()] = std::make_unique<LSPDocumentClient>( this, doc.get() );
|
||||
mDocs.emplace_back( doc );
|
||||
mDocs.emplace_back( doc.get() );
|
||||
doc->registerClient( mClients[doc.get()].get() );
|
||||
return true;
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::cancel( int reqid ) {
|
||||
if ( mHandlers.erase( reqid ) > 0 ) {
|
||||
auto params = json{ MEMBER_ID, reqid };
|
||||
return write( newRequest( "$/cancelRequest", params ) );
|
||||
}
|
||||
return RequestHandle();
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::write( const json& msg,
|
||||
const GenericReplyHandler& h,
|
||||
const GenericReplyHandler& eh,
|
||||
@@ -157,6 +172,85 @@ LSPClientServer::RequestHandle LSPClientServer::didOpen( const URI& document,
|
||||
return send( newRequest( "textDocument/didOpen", params ) );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::didOpen( TextDocument* doc, int version ) {
|
||||
if ( doc->isDirty() ) {
|
||||
IOStreamString text;
|
||||
doc->save( text, true );
|
||||
return didOpen( doc->getURI(), text.getStream(), version );
|
||||
} else {
|
||||
std::string text;
|
||||
FileSystem::fileGet( doc->getFilePath(), text );
|
||||
return didOpen( doc->getURI(), text, version );
|
||||
}
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle 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 ) {
|
||||
return didSave( doc->getURI(), doc->getText( doc->getDocRange() ).toUtf8() );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::didChange( const URI& document, int version,
|
||||
const std::string& text ) {
|
||||
auto params = textDocumentParams( document, version );
|
||||
if ( !text.empty() )
|
||||
params["contentChanges"] = { json{ MEMBER_TEXT, text } };
|
||||
return send( newRequest( "textDocument/didChange", params ) );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::didChange( TextDocument* doc ) {
|
||||
Lock l( mClientsMutex );
|
||||
auto it = mClients.find( doc );
|
||||
if ( it != mClients.end() )
|
||||
return didChange( doc->getURI(), it->second->getVersion(),
|
||||
doc->getText( doc->getDocRange() ).toUtf8() );
|
||||
return RequestHandle();
|
||||
}
|
||||
|
||||
void LSPClientServer::updateDirty() {
|
||||
Lock l( mClientsMutex );
|
||||
for ( auto& client : mClients ) {
|
||||
if ( client.second->isDirty() ) {
|
||||
TextDocument* doc = client.first;
|
||||
mManager->getThreadPool()->run( [this, doc]() { didChange( doc ); } );
|
||||
client.second->resetDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::didClose( const URI& document ) {
|
||||
auto params = textDocumentParams( document );
|
||||
return send( newRequest( "textDocument/didClose", params ) );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::didClose( TextDocument* doc ) {
|
||||
auto ret = didClose( doc->getURI() );
|
||||
Lock l( mClientsMutex );
|
||||
if ( mClients.erase( doc ) > 0 ) {
|
||||
auto it = std::find( mDocs.begin(), mDocs.end(), doc );
|
||||
if ( it != mDocs.end() )
|
||||
mDocs.erase( it );
|
||||
// No more docs are being used, close the LSP
|
||||
if ( mDocs.empty() )
|
||||
mManager->notifyClose( mId );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
LSPClientServerManager* LSPClientServer::getManager() const {
|
||||
return mManager;
|
||||
}
|
||||
|
||||
const std::shared_ptr<ThreadPool>& LSPClientServer::getThreadPool() const {
|
||||
return mManager->getThreadPool();
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentSymbols( const URI& document,
|
||||
const GenericReplyHandler& h,
|
||||
const GenericReplyHandler& eh ) {
|
||||
|
||||
@@ -18,6 +18,8 @@ using namespace EE::UI::Doc;
|
||||
|
||||
namespace ecode {
|
||||
|
||||
class LSPClientServerManager;
|
||||
|
||||
class LSPClientServer {
|
||||
public:
|
||||
template <typename T> using ReplyHandler = std::function<void( const T& )>;
|
||||
@@ -37,7 +39,8 @@ class LSPClientServer {
|
||||
}
|
||||
};
|
||||
|
||||
LSPClientServer( const LSPDefinition& lsp, const std::string& rootPath );
|
||||
LSPClientServer( LSPClientServerManager* manager, const String::HashType& id,
|
||||
const LSPDefinition& lsp, const std::string& rootPath );
|
||||
|
||||
~LSPClientServer();
|
||||
|
||||
@@ -45,27 +48,52 @@ class LSPClientServer {
|
||||
|
||||
bool registerDoc( const std::shared_ptr<TextDocument>& doc );
|
||||
|
||||
int cancel( int id ) { return id; }
|
||||
LSPClientServerManager* getManager() const;
|
||||
|
||||
const std::shared_ptr<ThreadPool>& getThreadPool() const;
|
||||
|
||||
LSPClientServer::RequestHandle cancel( int id );
|
||||
|
||||
RequestHandle send( const json& msg, const GenericReplyHandler& h = nullptr,
|
||||
const GenericReplyHandler& eh = nullptr );
|
||||
|
||||
LSPClientServer::RequestHandle didOpen( const URI& document, const std::string& text,
|
||||
int version );
|
||||
|
||||
const LSPDefinition& getDefinition() const { return mLSP; }
|
||||
|
||||
LSPClientServer::RequestHandle documentSymbols( const URI& document,
|
||||
const GenericReplyHandler& h,
|
||||
const GenericReplyHandler& eh );
|
||||
|
||||
LSPClientServer::RequestHandle didOpen( const URI& document, const std::string& text,
|
||||
int version );
|
||||
|
||||
LSPClientServer::RequestHandle didOpen( TextDocument* doc, int version );
|
||||
|
||||
LSPClientServer::RequestHandle didSave( const URI& document, const std::string& text );
|
||||
|
||||
LSPClientServer::RequestHandle didSave( TextDocument* doc );
|
||||
|
||||
LSPClientServer::RequestHandle didClose( const URI& document );
|
||||
|
||||
LSPClientServer::RequestHandle didClose( TextDocument* document );
|
||||
|
||||
LSPClientServer::RequestHandle didChange( const URI& document, int version,
|
||||
const std::string& text );
|
||||
|
||||
LSPClientServer::RequestHandle didChange( TextDocument* doc );
|
||||
|
||||
void updateDirty();
|
||||
|
||||
protected:
|
||||
LSPClientServerManager* mManager{ nullptr };
|
||||
String::HashType mId;
|
||||
LSPDefinition mLSP;
|
||||
std::string mRootPath;
|
||||
Process mProcess;
|
||||
std::vector<std::shared_ptr<TextDocument>> mDocs;
|
||||
std::vector<TextDocument*> mDocs;
|
||||
std::map<TextDocument*, std::unique_ptr<LSPDocumentClient>> mClients;
|
||||
std::map<int, std::pair<GenericReplyHandler, GenericReplyHandler>> mHandlers;
|
||||
Mutex mClientsMutex;
|
||||
|
||||
int mLastMsgId{ 0 };
|
||||
|
||||
void readStdOut( const char* bytes, size_t n );
|
||||
|
||||
@@ -39,9 +39,10 @@ LSPClientServerManager::supportsLSP( const std::shared_ptr<TextDocument>& doc )
|
||||
return lsps;
|
||||
}
|
||||
|
||||
std::shared_ptr<LSPClientServer>
|
||||
LSPClientServerManager::runLSPServer( const LSPDefinition& lsp, const std::string& rootPath ) {
|
||||
auto server = std::make_shared<LSPClientServer>( lsp, rootPath );
|
||||
std::unique_ptr<LSPClientServer>
|
||||
LSPClientServerManager::runLSPServer( const String::HashType& id, const LSPDefinition& lsp,
|
||||
const std::string& rootPath ) {
|
||||
auto server = std::make_unique<LSPClientServer>( this, id, lsp, rootPath );
|
||||
server->start();
|
||||
return server;
|
||||
}
|
||||
@@ -76,17 +77,23 @@ void LSPClientServerManager::tryRunServer( const std::shared_ptr<TextDocument>&
|
||||
auto lspName = lsp.name.empty() ? lsp.command : lsp.name;
|
||||
String::HashType id = String::hash( lspName + "|" + lsp.language + "|" + rootPath );
|
||||
auto clientIt = mClients.find( id );
|
||||
std::shared_ptr<LSPClientServer> server;
|
||||
LSPClientServer* server = nullptr;
|
||||
if ( clientIt == mClients.end() ) {
|
||||
server = runLSPServer( lsp, rootPath );
|
||||
if ( server.use_count() )
|
||||
mClients[id] = server;
|
||||
std::unique_ptr<LSPClientServer> serverUP = runLSPServer( id, lsp, rootPath );
|
||||
if ( ( server = serverUP.get() ) )
|
||||
mClients[id] = std::move( serverUP );
|
||||
} else {
|
||||
server = clientIt->second;
|
||||
server = clientIt->second.get();
|
||||
}
|
||||
if ( server.use_count() ) {
|
||||
if ( server )
|
||||
server->registerDoc( doc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LSPClientServerManager::notifyClose( const String::HashType& id ) {
|
||||
auto it = mClients.find( id );
|
||||
if ( it != mClients.end() ) {
|
||||
mClients.erase( it );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,4 +105,13 @@ size_t LSPClientServerManager::clientCount() const {
|
||||
return mClients.size();
|
||||
}
|
||||
|
||||
const std::shared_ptr<ThreadPool>& LSPClientServerManager::getThreadPool() const {
|
||||
return mPool;
|
||||
}
|
||||
|
||||
void LSPClientServerManager::updateDirty() {
|
||||
for ( auto& server : mClients )
|
||||
server.second->updateDirty();
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -20,19 +20,28 @@ class LSPClientServerManager {
|
||||
|
||||
size_t clientCount() const;
|
||||
|
||||
const std::shared_ptr<ThreadPool>& getThreadPool() const;
|
||||
|
||||
void updateDirty();
|
||||
|
||||
protected:
|
||||
friend class LSPClientServer;
|
||||
|
||||
std::shared_ptr<ThreadPool> mPool;
|
||||
std::map<String::HashType, std::shared_ptr<LSPClientServer>> mClients;
|
||||
std::map<String::HashType, std::unique_ptr<LSPClientServer>> mClients;
|
||||
std::vector<LSPDefinition> mLSPs;
|
||||
|
||||
std::vector<LSPDefinition> supportsLSP( const std::shared_ptr<TextDocument>& doc );
|
||||
|
||||
std::shared_ptr<LSPClientServer> runLSPServer( const LSPDefinition& lsp,
|
||||
std::unique_ptr<LSPClientServer> runLSPServer( const String::HashType& id,
|
||||
const LSPDefinition& lsp,
|
||||
const std::string& rootPath );
|
||||
|
||||
std::string findRootPath( const LSPDefinition& lsp, const std::shared_ptr<TextDocument>& doc );
|
||||
|
||||
void tryRunServer( const std::shared_ptr<TextDocument>& doc );
|
||||
|
||||
void notifyClose( const String::HashType& id );
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -10,17 +10,13 @@ namespace ecode {
|
||||
LSPDocumentClient::LSPDocumentClient( LSPClientServer* server, TextDocument* doc ) :
|
||||
mServer( server ), mDoc( doc ) {
|
||||
notifyOpen();
|
||||
mServer->documentSymbols(
|
||||
mDoc->getURI(),
|
||||
[&]( const json& ) {
|
||||
|
||||
},
|
||||
[&]( const json& ) {
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
void LSPDocumentClient::onDocumentTextChanged() {}
|
||||
void LSPDocumentClient::onDocumentTextChanged() {
|
||||
mModified = true;
|
||||
++mVersion;
|
||||
mLastModified.restart();
|
||||
}
|
||||
|
||||
void LSPDocumentClient::onDocumentUndoRedo( const TextDocument::UndoRedo& /*eventType*/ ) {}
|
||||
|
||||
@@ -33,24 +29,42 @@ void LSPDocumentClient::onDocumentLineCountChange( const size_t& /*lastCount*/,
|
||||
|
||||
void LSPDocumentClient::onDocumentLineChanged( const Int64& /*lineIndex*/ ) {}
|
||||
|
||||
void LSPDocumentClient::onDocumentSaved( TextDocument* ) {}
|
||||
void LSPDocumentClient::onDocumentSaved( TextDocument* ) {
|
||||
mServer->getThreadPool()->run( [&]() { mServer->didSave( mDoc ); } );
|
||||
}
|
||||
|
||||
void LSPDocumentClient::onDocumentClosed( TextDocument* ) {}
|
||||
void LSPDocumentClient::onDocumentClosed( TextDocument* ) {
|
||||
mServer->didClose( mDoc );
|
||||
mDoc = nullptr;
|
||||
}
|
||||
|
||||
void LSPDocumentClient::onDocumentDirtyOnFileSystem( TextDocument* ) {}
|
||||
|
||||
void LSPDocumentClient::onDocumentMoved( TextDocument* ) {}
|
||||
|
||||
bool LSPDocumentClient::isDirty() const {
|
||||
return mModified && mLastModified.getElapsedTime() > Milliseconds( 250.f );
|
||||
}
|
||||
|
||||
void LSPDocumentClient::resetDirty() {
|
||||
mModified = false;
|
||||
}
|
||||
|
||||
TextDocument* LSPDocumentClient::getDoc() const {
|
||||
return mDoc;
|
||||
}
|
||||
|
||||
LSPClientServer* LSPDocumentClient::getServer() const {
|
||||
return mServer;
|
||||
}
|
||||
|
||||
int LSPDocumentClient::getVersion() const {
|
||||
return mVersion;
|
||||
}
|
||||
|
||||
void LSPDocumentClient::notifyOpen() {
|
||||
if ( mDoc->isDirty() ) {
|
||||
IOStreamString text;
|
||||
mDoc->save( text, true );
|
||||
mServer->didOpen( mDoc->getURI(), text.getStream(), mVersion );
|
||||
} else {
|
||||
std::string text;
|
||||
FileSystem::fileGet( mDoc->getFilePath(), text );
|
||||
mServer->didOpen( mDoc->getURI(), text, mVersion );
|
||||
}
|
||||
eeASSERT( mDoc );
|
||||
mServer->didOpen( mDoc, ++mVersion );
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#ifndef ECODE_LSPDOCUMENTCLIENT_HPP
|
||||
#define ECODE_LSPDOCUMENTCLIENT_HPP
|
||||
|
||||
#include <eepp/system/clock.hpp>
|
||||
#include <eepp/ui/doc/textdocument.hpp>
|
||||
|
||||
using namespace EE;
|
||||
using namespace EE::System;
|
||||
using namespace EE::UI::Doc;
|
||||
|
||||
namespace ecode {
|
||||
@@ -25,11 +27,24 @@ class LSPDocumentClient : public TextDocument::Client {
|
||||
virtual void onDocumentDirtyOnFileSystem( TextDocument* );
|
||||
virtual void onDocumentMoved( TextDocument* );
|
||||
|
||||
bool isDirty() const;
|
||||
|
||||
void notifyOpen();
|
||||
|
||||
void resetDirty();
|
||||
|
||||
TextDocument* getDoc() const;
|
||||
|
||||
LSPClientServer* getServer() const;
|
||||
|
||||
int getVersion() const;
|
||||
|
||||
protected:
|
||||
LSPClientServer* mServer{ nullptr };
|
||||
TextDocument* mDoc{ nullptr };
|
||||
bool mModified{ false };
|
||||
int mVersion{ 0 };
|
||||
Clock mLastModified;
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
Reference in New Issue
Block a user