More LSP work.

This commit is contained in:
Martín Lucas Golini
2022-10-30 15:49:02 -03:00
parent 79b1aeb725
commit ec847e8678
12 changed files with 251 additions and 59 deletions

View File

@@ -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" );

View File

@@ -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; }

View File

@@ -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 ) {

View File

@@ -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 );

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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