mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-08-18 06:55:48 +03:00
ecode: LinterPlugin now supports LSP diagnostics. LSP client plugin refactor.
This commit is contained in:
@@ -89,6 +89,27 @@ void LinterPlugin::loadLinterConfig( const std::string& path ) {
|
||||
auto& config = j["config"];
|
||||
if ( config.contains( "delay_time" ) )
|
||||
setDelayTime( Time::fromString( config["delay_time"].get<std::string>() ) );
|
||||
if ( config.contains( "enable_lsp_diagnostics" ) &&
|
||||
config["enable_lsp_diagnostics"].is_boolean() )
|
||||
setEnableLSPDiagnostics( config["enable_lsp_diagnostics"].get<bool>() );
|
||||
if ( config.contains( "disable_lsp_languages" ) &&
|
||||
config["disable_lsp_languages"].is_array() ) {
|
||||
const auto& langs = config["disable_lsp_languages"];
|
||||
try {
|
||||
mLSPLanguagesDisabled.clear();
|
||||
for ( const auto& lang : langs ) {
|
||||
if ( lang.is_string() ) {
|
||||
std::string lg = lang.get<std::string>();
|
||||
if ( !lg.empty() )
|
||||
mLSPLanguagesDisabled.insert( lg );
|
||||
}
|
||||
}
|
||||
} catch ( const json::exception& e ) {
|
||||
Log::debug(
|
||||
"LinterPlugin::loadLinterConfig: Error parsing disable_lsp_languages: %s",
|
||||
e.what() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !j.contains( "linters" ) )
|
||||
@@ -166,7 +187,107 @@ void LinterPlugin::loadLinterConfig( const std::string& path ) {
|
||||
}
|
||||
}
|
||||
|
||||
LinterType getLinterTypeFromSeverity( const LSPDiagnosticSeverity& severity ) {
|
||||
switch ( severity ) {
|
||||
case LSPDiagnosticSeverity::Error:
|
||||
return LinterType::Error;
|
||||
case LSPDiagnosticSeverity::Warning:
|
||||
return LinterType::Warning;
|
||||
default:
|
||||
return LinterType::Notice;
|
||||
}
|
||||
}
|
||||
|
||||
void LinterPlugin::eraseMatchesFromOrigin( TextDocument* doc, const MatchOrigin& origin ) {
|
||||
Lock matchesLock( mMatchesMutex );
|
||||
auto& docMatches = mMatches[doc];
|
||||
|
||||
std::vector<Int64> emptyMatchLines;
|
||||
for ( auto& matchLine : docMatches ) {
|
||||
bool found;
|
||||
do {
|
||||
found = false;
|
||||
auto it = matchLine.second.begin();
|
||||
for ( ; it != matchLine.second.end(); ++it ) {
|
||||
if ( it->origin == origin ) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( found ) {
|
||||
matchLine.second.erase( it );
|
||||
if ( matchLine.second.empty() )
|
||||
emptyMatchLines.push_back( matchLine.first );
|
||||
}
|
||||
} while ( found );
|
||||
}
|
||||
|
||||
for ( const auto& line : emptyMatchLines )
|
||||
docMatches.erase( line );
|
||||
}
|
||||
|
||||
void LinterPlugin::insertMatches( TextDocument* doc,
|
||||
std::map<Int64, std::vector<LinterMatch>>& matches ) {
|
||||
Lock matchesLock( mMatchesMutex );
|
||||
auto& docMatches = mMatches[doc];
|
||||
for ( auto& match : matches ) {
|
||||
std::vector<LinterMatch>& vec = docMatches[match.first];
|
||||
vec.insert( vec.end(), match.second.begin(), match.second.end() );
|
||||
}
|
||||
}
|
||||
|
||||
void LinterPlugin::setMatches( TextDocument* doc, const MatchOrigin& origin,
|
||||
std::map<Int64, std::vector<LinterMatch>>& matches ) {
|
||||
if ( !mEnableLSPDiagnostics ) {
|
||||
Lock matchesLock( mMatchesMutex );
|
||||
mMatches[doc] = std::move( matches );
|
||||
} else {
|
||||
eraseMatchesFromOrigin( doc, origin );
|
||||
insertMatches( doc, matches );
|
||||
}
|
||||
|
||||
invalidateEditors( doc );
|
||||
}
|
||||
|
||||
void LinterPlugin::processNotification( const PluginManager::Notification& notification ) {
|
||||
if ( !mEnableLSPDiagnostics ||
|
||||
notification.type != PluginManager::NotificationType::PublishDiagnostics ||
|
||||
notification.format != PluginManager::NotificationFormat::Diagnostics )
|
||||
return;
|
||||
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;
|
||||
|
||||
std::map<Int64, std::vector<LinterMatch>> matches;
|
||||
|
||||
for ( const auto& diag : diags.diagnostics ) {
|
||||
LinterMatch match;
|
||||
match.range = diag.range;
|
||||
match.text = diag.message;
|
||||
match.type = getLinterTypeFromSeverity( diag.severity );
|
||||
match.lineCache = doc->line( match.range.start().line() ).getHash();
|
||||
match.origin = MatchOrigin::Diagnostics;
|
||||
matches[match.range.start().line()].emplace_back( std::move( match ) );
|
||||
}
|
||||
|
||||
setMatches( doc, MatchOrigin::Diagnostics, matches );
|
||||
}
|
||||
|
||||
TextDocument* LinterPlugin::getDocumentFromURI( const URI& uri ) {
|
||||
for ( TextDocument* doc : mDocs ) {
|
||||
if ( doc->getURI() == uri )
|
||||
return doc;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void LinterPlugin::load( const PluginManager* pluginManager ) {
|
||||
pluginManager->subscribeNotifications(
|
||||
this, [&]( const auto& notification ) { processNotification( notification ); } );
|
||||
std::vector<std::string> paths;
|
||||
std::string path( pluginManager->getResourcesPath() + "plugins/linters.json" );
|
||||
if ( FileSystem::fileExists( path ) )
|
||||
@@ -274,6 +395,14 @@ void LinterPlugin::setDelayTime( const Time& delayTime ) {
|
||||
mDelayTime = delayTime;
|
||||
}
|
||||
|
||||
bool LinterPlugin::getEnableLSPDiagnostics() const {
|
||||
return mEnableLSPDiagnostics;
|
||||
}
|
||||
|
||||
void LinterPlugin::setEnableLSPDiagnostics( bool enableLSPDiagnostics ) {
|
||||
mEnableLSPDiagnostics = enableLSPDiagnostics;
|
||||
}
|
||||
|
||||
void LinterPlugin::lintDoc( std::shared_ptr<TextDocument> doc ) {
|
||||
ScopedOp op(
|
||||
[&]() {
|
||||
@@ -395,13 +524,35 @@ void LinterPlugin::runLinter( std::shared_ptr<TextDocument> doc, const Linter& l
|
||||
if ( linter.columnsStartAtZero )
|
||||
col++;
|
||||
}
|
||||
linterMatch.pos = { line - 1, col > 0 ? col - 1 : 0 };
|
||||
linterMatch.lineCache = doc->line( line - 1 ).getHash();
|
||||
linterMatch.range.setStart( { line - 1, col > 0 ? col - 1 : 0 } );
|
||||
|
||||
const String& text = doc->line( linterMatch.range.start().line() ).getText();
|
||||
size_t minCol =
|
||||
text.find_first_not_of( " \t\f\v\n\r", linterMatch.range.start().column() );
|
||||
if ( minCol == String::InvalidPos )
|
||||
minCol = linterMatch.range.start().column();
|
||||
minCol = std::max( (Int64)minCol, linterMatch.range.start().column() );
|
||||
if ( minCol >= text.size() )
|
||||
minCol = linterMatch.range.start().column();
|
||||
if ( minCol >= text.size() )
|
||||
minCol = text.size() - 1;
|
||||
linterMatch.range.setStart(
|
||||
{ linterMatch.range.start().line(), (Int64)minCol } );
|
||||
TextPosition endPos;
|
||||
endPos = ( minCol < text.size() - 1 )
|
||||
? doc->nextWordBoundary(
|
||||
{ linterMatch.range.start().line(), (Int64)minCol } )
|
||||
: doc->previousWordBoundary(
|
||||
{ linterMatch.range.start().line(), (Int64)minCol } );
|
||||
|
||||
linterMatch.range.setEnd( endPos );
|
||||
linterMatch.range = linterMatch.range.normalized();
|
||||
linterMatch.lineCache = doc->line( linterMatch.range.start().line() ).getHash();
|
||||
bool skip = false;
|
||||
|
||||
if ( linter.deduplicate && matches.find( line - 1 ) != matches.end() ) {
|
||||
for ( auto& match : matches[line - 1] ) {
|
||||
if ( match.pos == linterMatch.pos ) {
|
||||
if ( match.range == linterMatch.range ) {
|
||||
match.text += "\n" + linterMatch.text;
|
||||
skip = true;
|
||||
break;
|
||||
@@ -433,12 +584,7 @@ void LinterPlugin::runLinter( std::shared_ptr<TextDocument> doc, const Linter& l
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Lock matchesLock( mMatchesMutex );
|
||||
mMatches[doc.get()] = std::move( matches );
|
||||
}
|
||||
|
||||
invalidateEditors( doc.get() );
|
||||
setMatches( doc.get(), MatchOrigin::Linter, matches );
|
||||
|
||||
Log::info( "LinterPlugin::runLinter for %s took %.2fms. Found: %d matches. Errors: %d, "
|
||||
"Warnings: %d, Notices: %d.",
|
||||
@@ -481,33 +627,9 @@ void LinterPlugin::drawAfterLineText( UICodeEditor* editor, const Int64& index,
|
||||
line.setStyleConfig( editor->getFontStyleConfig() );
|
||||
line.setColor(
|
||||
editor->getColorScheme().getEditorSyntaxStyle( getMatchString( match.type ) ).color );
|
||||
const String& text = doc->line( index ).getText();
|
||||
size_t minCol = text.find_first_not_of( " \t\f\v\n\r", match.pos.column() );
|
||||
if ( minCol == String::InvalidPos )
|
||||
minCol = match.pos.column();
|
||||
minCol = std::max( (Int64)minCol, match.pos.column() );
|
||||
if ( minCol >= text.size() )
|
||||
minCol = match.pos.column();
|
||||
if ( minCol >= text.size() )
|
||||
minCol = text.size() - 1;
|
||||
|
||||
Int64 strSize = 0;
|
||||
TextPosition endPos;
|
||||
Vector2f pos;
|
||||
|
||||
if ( minCol < text.size() - 1 ) {
|
||||
endPos = doc->nextWordBoundary( { match.pos.line(), (Int64)minCol } );
|
||||
strSize = eemax( (Int64)0, static_cast<Int64>( endPos.column() - minCol ) );
|
||||
pos = { position.x + editor->getXOffsetCol( { match.pos.line(), (Int64)minCol } ),
|
||||
position.y };
|
||||
} else {
|
||||
endPos = doc->previousWordBoundary( { match.pos.line(), (Int64)minCol } );
|
||||
strSize = eemax( (Int64)0, static_cast<Int64>( minCol - endPos.column() ) );
|
||||
pos = { position.x +
|
||||
editor->getXOffsetCol( { match.pos.line(), (Int64)endPos.column() } ),
|
||||
position.y };
|
||||
}
|
||||
|
||||
Int64 strSize = match.range.end().column() - match.range.start().column();
|
||||
Vector2f pos = { position.x + editor->getXOffsetCol( match.range.start() ), position.y };
|
||||
if ( strSize == 0 ) {
|
||||
strSize = 1;
|
||||
pos = { position.x, position.y };
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace ecode {
|
||||
|
||||
enum class LinterType { Notice, Warning, Error };
|
||||
|
||||
enum class MatchOrigin { Linter, Diagnostics };
|
||||
|
||||
struct Linter {
|
||||
std::vector<std::string> files;
|
||||
std::vector<std::string> warningPattern;
|
||||
@@ -36,10 +38,11 @@ struct Linter {
|
||||
|
||||
struct LinterMatch {
|
||||
std::string text;
|
||||
TextPosition pos;
|
||||
String::HashType lineCache;
|
||||
std::map<UICodeEditor*, Rectf> box;
|
||||
TextRange range;
|
||||
LinterType type{ LinterType::Error };
|
||||
String::HashType lineCache;
|
||||
MatchOrigin origin{ MatchOrigin::Linter };
|
||||
std::map<UICodeEditor*, Rectf> box;
|
||||
};
|
||||
|
||||
class LinterPlugin : public UICodeEditorPlugin {
|
||||
@@ -88,6 +91,10 @@ class LinterPlugin : public UICodeEditorPlugin {
|
||||
|
||||
void setDelayTime( const Time& delayTime );
|
||||
|
||||
bool getEnableLSPDiagnostics() const;
|
||||
|
||||
void setEnableLSPDiagnostics( bool enableLSPDiagnostics );
|
||||
|
||||
protected:
|
||||
std::shared_ptr<ThreadPool> mPool;
|
||||
std::vector<Linter> mLinters;
|
||||
@@ -107,6 +114,8 @@ class LinterPlugin : public UICodeEditorPlugin {
|
||||
bool mReady{ false };
|
||||
bool mShuttingDown{ false };
|
||||
bool mHoveringMatch{ false };
|
||||
bool mEnableLSPDiagnostics{ true };
|
||||
std::set<std::string> mLSPLanguagesDisabled;
|
||||
|
||||
LinterPlugin( const PluginManager* pluginManager );
|
||||
|
||||
@@ -130,6 +139,17 @@ class LinterPlugin : public UICodeEditorPlugin {
|
||||
void loadLinterConfig( const std::string& path );
|
||||
|
||||
size_t linterFilePatternPosition( const std::vector<std::string>& patterns );
|
||||
|
||||
void processNotification( const PluginManager::Notification& notification );
|
||||
|
||||
TextDocument* getDocumentFromURI( const URI& uri );
|
||||
|
||||
void eraseMatchesFromOrigin( TextDocument* doc, const MatchOrigin& origin );
|
||||
|
||||
void insertMatches( TextDocument* doc, std::map<Int64, std::vector<LinterMatch>>& matches );
|
||||
|
||||
void setMatches( TextDocument* doc, const MatchOrigin& origin,
|
||||
std::map<Int64, std::vector<LinterMatch>>& matches );
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -25,7 +25,7 @@ LSPClientPlugin::LSPClientPlugin( const PluginManager* pluginManager ) :
|
||||
LSPClientPlugin::~LSPClientPlugin() {
|
||||
mClosing = true;
|
||||
Lock l( mDocMutex );
|
||||
for ( const auto &editor : mEditors ) {
|
||||
for ( const auto& editor : mEditors ) {
|
||||
for ( auto& kb : mKeyBindings ) {
|
||||
editor.first->getKeyBindings().removeCommandKeybind( kb.first );
|
||||
if ( editor.first->hasDocument() )
|
||||
@@ -47,11 +47,10 @@ void LSPClientPlugin::update( UICodeEditor* ) {
|
||||
mClientManager.updateDirty();
|
||||
}
|
||||
|
||||
void LSPClientPlugin::processNotification( PluginManager::Notification noti,
|
||||
const nlohmann::json& obj ) {
|
||||
switch ( noti ) {
|
||||
void LSPClientPlugin::processNotification( const PluginManager::Notification& notification ) {
|
||||
switch ( notification.type ) {
|
||||
case PluginManager::WorkspaceFolderChanged: {
|
||||
mClientManager.didChangeWorkspaceFolders( obj["folder"] );
|
||||
mClientManager.didChangeWorkspaceFolders( notification.asJSON()["folder"] );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -61,7 +60,7 @@ void LSPClientPlugin::processNotification( PluginManager::Notification noti,
|
||||
|
||||
void LSPClientPlugin::load( const PluginManager* pluginManager ) {
|
||||
pluginManager->subscribeNotifications(
|
||||
this, [&]( auto noti, auto obj ) { processNotification( noti, obj ); } );
|
||||
this, [&]( const auto& notification ) { processNotification( notification ); } );
|
||||
std::vector<std::string> paths;
|
||||
std::string path( pluginManager->getResourcesPath() + "plugins/lspclient.json" );
|
||||
if ( FileSystem::fileExists( path ) )
|
||||
@@ -137,6 +136,28 @@ void LSPClientPlugin::loadLSPConfig( std::vector<LSPDefinition>& lsps, const std
|
||||
auto& servers = j["servers"];
|
||||
|
||||
for ( auto& obj : servers ) {
|
||||
// Allow disabling an LSP by redeclaring it in the user configuration file.
|
||||
if ( obj.contains( "name" ) && obj.contains( "disabled" ) &&
|
||||
obj.at( "disabled" ).is_boolean() ) {
|
||||
for ( auto& lsp : lsps ) {
|
||||
if ( lsp.name == obj["name"] ) {
|
||||
lsp.disabled = obj["disabled"].get<bool>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow setting user command paramaters for an already declared LSP
|
||||
if ( obj.contains( "name" ) && obj.contains( "command_parameters" ) &&
|
||||
obj.at( "command_parameters" ).is_string() ) {
|
||||
for ( auto& lsp : lsps ) {
|
||||
if ( lsp.name == obj["name"] ) {
|
||||
lsp.commandParameters = obj.value( "command_parameters", "" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !obj.contains( "language" ) || !obj.contains( "file_patterns" ) ) {
|
||||
Log::warning( "LSP server without language or file_patterns, ignored..." );
|
||||
continue;
|
||||
@@ -173,8 +194,8 @@ void LSPClientPlugin::loadLSPConfig( std::vector<LSPDefinition>& lsps, const std
|
||||
lsp.name = obj["name"];
|
||||
}
|
||||
|
||||
if ( obj.contains( "url" ) )
|
||||
lsp.url = obj["url"];
|
||||
lsp.url = obj.value( "url", "" );
|
||||
lsp.commandParameters = obj.value( "command_parameters", lsp.commandParameters );
|
||||
|
||||
auto fp = obj["file_patterns"];
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ class LSPClientPlugin : public UICodeEditorPlugin {
|
||||
size_t lspFilePatternPosition( const std::vector<LSPDefinition>& lsps,
|
||||
const std::vector<std::string>& patterns );
|
||||
|
||||
void processNotification( PluginManager::Notification, const nlohmann::json& );
|
||||
void processNotification( const PluginManager::Notification& notification );
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "lspclientserver.hpp"
|
||||
#include "lspclientplugin.hpp"
|
||||
#include "lspclientservermanager.hpp"
|
||||
#include <algorithm>
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
@@ -27,7 +28,7 @@ static const char* MEMBER_RESULT = "result";
|
||||
static const char* MEMBER_START = "start";
|
||||
static const char* MEMBER_END = "end";
|
||||
static const char* MEMBER_POSITION = "position";
|
||||
// static const char* MEMBER_POSITIONS = "positions";
|
||||
static const char* MEMBER_POSITIONS = "positions";
|
||||
static const char* MEMBER_LOCATION = "location";
|
||||
static const char* MEMBER_RANGE = "range";
|
||||
static const char* MEMBER_LINE = "line";
|
||||
@@ -44,8 +45,8 @@ static const char* MEMBER_DIAGNOSTICS = "diagnostics";
|
||||
static const char* MEMBER_TARGET_URI = "targetUri";
|
||||
static const char* MEMBER_TARGET_RANGE = "targetRange";
|
||||
static const char* MEMBER_TARGET_SELECTION_RANGE = "targetSelectionRange";
|
||||
// static const char* MEMBER_PREVIOUS_RESULT_ID = "previousResultId";
|
||||
// static const char* MEMBER_QUERY = "query";
|
||||
static const char* MEMBER_PREVIOUS_RESULT_ID = "previousResultId";
|
||||
static const char* MEMBER_QUERY = "query";
|
||||
|
||||
static json newRequest( const std::string& method, const json& params = json{} ) {
|
||||
json j;
|
||||
@@ -175,6 +176,20 @@ static json textDocumentPositionParams( const URI& document, TextPosition pos )
|
||||
return params;
|
||||
}
|
||||
|
||||
static json toJson( const std::vector<TextPosition>& positions ) {
|
||||
json result;
|
||||
for ( const auto& position : positions )
|
||||
result.push_back( toJson( position ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
static json textDocumentPositionsParams( const URI& document,
|
||||
const std::vector<TextPosition>& positions ) {
|
||||
auto params = textDocumentParams( document );
|
||||
params[MEMBER_POSITIONS] = toJson( positions );
|
||||
return params;
|
||||
}
|
||||
|
||||
static void fromJson( std::vector<char>& trigger, const json& json ) {
|
||||
if ( !json.empty() ) {
|
||||
const auto triggersArray = json;
|
||||
@@ -358,6 +373,37 @@ static std::vector<LSPSymbolInformation> parseDocumentSymbols( const json& resul
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::vector<LSPSymbolInformation> parseWorkspaceSymbols( const json& res ) {
|
||||
std::vector<LSPSymbolInformation> symbols;
|
||||
symbols.reserve( res.size() );
|
||||
|
||||
std::transform(
|
||||
res.cbegin(), res.cend(), std::back_inserter( symbols ), []( const json& symbol ) {
|
||||
LSPSymbolInformation symInfo;
|
||||
|
||||
const auto location = symbol.at( MEMBER_LOCATION );
|
||||
const auto mrange = symbol.contains( MEMBER_RANGE ) ? symbol.at( MEMBER_RANGE )
|
||||
: location.at( MEMBER_RANGE );
|
||||
|
||||
auto containerName = symbol.value( "containerName", "" );
|
||||
if ( !containerName.empty() )
|
||||
containerName.append( "::" );
|
||||
symInfo.name = containerName + symbol.value( "name", "" );
|
||||
symInfo.kind = (LSPSymbolKind)symbol.value( MEMBER_KIND, 1 );
|
||||
symInfo.range = parseRange( mrange );
|
||||
symInfo.url = URI( location.value( MEMBER_URI, "" ) );
|
||||
symInfo.score = symbol.value( "score", 0.0 );
|
||||
return symInfo;
|
||||
} );
|
||||
|
||||
std::sort( symbols.begin(), symbols.end(),
|
||||
[]( const LSPSymbolInformation& l, const LSPSymbolInformation& r ) {
|
||||
return l.score > r.score;
|
||||
} );
|
||||
|
||||
return symbols;
|
||||
}
|
||||
|
||||
static LSPResponseError parseResponseError( const json& v ) {
|
||||
LSPResponseError ret;
|
||||
if ( v.is_object() ) {
|
||||
@@ -611,6 +657,32 @@ static std::vector<LSPCompletionItem> parseDocumentCompletion( const json& resul
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::shared_ptr<LSPSelectionRange> parseSelectionRange( const json& selectionRange ) {
|
||||
auto current = std::make_shared<LSPSelectionRange>( LSPSelectionRange{} );
|
||||
std::shared_ptr<LSPSelectionRange> ret = current;
|
||||
json selRange = std::move( selectionRange );
|
||||
|
||||
while ( selRange.is_object() ) {
|
||||
current->range = parseRange( selRange[MEMBER_RANGE] );
|
||||
if ( !selRange["parent"].is_object() ) {
|
||||
current->parent = nullptr;
|
||||
break;
|
||||
}
|
||||
selRange = selRange["parent"];
|
||||
current->parent = std::make_shared<LSPSelectionRange>( LSPSelectionRange{} );
|
||||
current = current->parent;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::vector<std::shared_ptr<LSPSelectionRange>>
|
||||
parseSelectionRanges( const json& selectionRanges ) {
|
||||
std::vector<std::shared_ptr<LSPSelectionRange>> ret;
|
||||
for ( const auto& selectionRange : selectionRanges )
|
||||
ret.push_back( parseSelectionRange( selectionRange ) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LSPClientServer::initialize() {
|
||||
json codeAction{ { "codeActionLiteralSupport",
|
||||
json{ { "codeActionKind", json{ { "valueSet", json::array() } } } } } };
|
||||
@@ -735,7 +807,7 @@ LSPClientServer::RequestHandle LSPClientServer::cancel( int reqid ) {
|
||||
LSPClientServer::RequestHandle LSPClientServer::write( const json& msg, const JsonReplyHandler& h,
|
||||
const JsonReplyHandler& eh, const int id ) {
|
||||
RequestHandle ret;
|
||||
ret.mServer = this;
|
||||
ret.server = this;
|
||||
|
||||
if ( !mProcess.isAlive() )
|
||||
return ret;
|
||||
@@ -746,7 +818,7 @@ LSPClientServer::RequestHandle LSPClientServer::write( const json& msg, const Js
|
||||
// notification == no handler
|
||||
if ( h ) {
|
||||
ob[MEMBER_ID] = ++mLastMsgId;
|
||||
ret.mId = mLastMsgId;
|
||||
ret.id = mLastMsgId;
|
||||
Lock l( mHandlersMutex );
|
||||
mHandlers[mLastMsgId] = { h, eh };
|
||||
} else if ( id ) {
|
||||
@@ -907,6 +979,21 @@ LSPClientServer::documentSymbols( const URI& document,
|
||||
} );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle 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::workspaceSymbol( const std::string& querySymbol,
|
||||
const SymbolInformationHandler& h ) {
|
||||
return workspaceSymbol( querySymbol, [h]( const json& json ) {
|
||||
if ( h )
|
||||
h( parseWorkspaceSymbols( json ) );
|
||||
} );
|
||||
}
|
||||
|
||||
void fromJson( LSPWorkDoneProgressValue& value, const json& json ) {
|
||||
if ( !json.empty() ) {
|
||||
auto ob = json;
|
||||
@@ -942,14 +1029,18 @@ static json newError( const LSPErrorCode& code, const std::string& msg ) {
|
||||
}
|
||||
|
||||
void LSPClientServer::publishDiagnostics( const json& msg ) {
|
||||
// should emmit event somewhere
|
||||
auto res = parsePublishDiagnostics( msg[MEMBER_PARAMS] );
|
||||
LSPPublishDiagnosticsParams res = parsePublishDiagnostics( msg[MEMBER_PARAMS] );
|
||||
if ( mManager && mManager->getPluginManager() && mManager->getPlugin() ) {
|
||||
mManager->getPluginManager()->pushNotification( mManager->getPlugin(),
|
||||
PluginManager::PublishDiagnostics,
|
||||
PluginManager::Diagnostics, &res );
|
||||
}
|
||||
Log::debug( "LSPClientServer::publishDiagnostics: %s - returned %zu items",
|
||||
res.uri.toString().c_str(), res.diagnostics.size() );
|
||||
}
|
||||
|
||||
void LSPClientServer::workDoneProgress( const LSPWorkDoneProgressParams& workDoneParams ) {
|
||||
// should emmit event somewhere
|
||||
// should emit event somewhere
|
||||
Log::debug( "LSPClientServer::workDoneProgress: %s",
|
||||
workDoneParams.token.is_string()
|
||||
? workDoneParams.token.get<std::string>().c_str()
|
||||
@@ -1187,11 +1278,6 @@ LSPClientServer::RequestHandle LSPClientServer::documentHover( const URI& docume
|
||||
} );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentHover( TextDocument* doc,
|
||||
const HoverHandler& h ) {
|
||||
return documentHover( doc->getURI(), doc->getSelection().start(), h );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle LSPClientServer::documentCompletion( const URI& document,
|
||||
const TextPosition& pos,
|
||||
const JsonReplyHandler& h ) {
|
||||
@@ -1208,4 +1294,38 @@ LSPClientServer::RequestHandle LSPClientServer::documentCompletion( const URI& d
|
||||
} );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
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::selectionRange( const URI& document, const std::vector<TextPosition>& positions,
|
||||
const SelectionRangeHandler& h ) {
|
||||
return selectionRange( document, positions, [h]( const json& json ) {
|
||||
if ( h )
|
||||
h( parseSelectionRanges( json ) );
|
||||
} );
|
||||
}
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
LSPClientServer::documentSemanticTokensFull( const URI& document, bool delta,
|
||||
const std::string& requestId, const TextRange& range,
|
||||
const JsonReplyHandler& h ) {
|
||||
auto params = textDocumentParams( document );
|
||||
// Delta
|
||||
if ( delta && !requestId.empty() ) {
|
||||
params[MEMBER_PREVIOUS_RESULT_ID] = requestId;
|
||||
return send( newRequest( "textDocument/semanticTokens/full/delta", params ), h );
|
||||
}
|
||||
// Range
|
||||
if ( range.isValid() ) {
|
||||
params[MEMBER_RANGE] = toJson( range );
|
||||
return send( newRequest( "textDocument/semanticTokens/range", params ), h );
|
||||
}
|
||||
|
||||
return send( newRequest( "textDocument/semanticTokens/full", params ), h );
|
||||
}
|
||||
} // namespace ecode
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef ECODE_LSPCLIENTSERVER_HPP
|
||||
#define ECODE_LSPCLIENTSERVER_HPP
|
||||
|
||||
#include "lspclientprotocol.hpp"
|
||||
#include "lspprotocol.hpp"
|
||||
#include "lspdefinition.hpp"
|
||||
#include "lspdocumentclient.hpp"
|
||||
#include <eepp/system/process.hpp>
|
||||
@@ -31,16 +31,19 @@ class LSPClientServer {
|
||||
using CodeActionHandler = ReplyHandler<std::vector<LSPCodeAction>>;
|
||||
using HoverHandler = ReplyHandler<LSPHover>;
|
||||
using CompletionHandler = ReplyHandler<std::vector<LSPCompletionItem>>;
|
||||
using SymbolInformationHandler = ReplyHandler<std::vector<LSPSymbolInformation>>;
|
||||
using SelectionRangeHandler = ReplyHandler<std::vector<std::shared_ptr<LSPSelectionRange>>>;
|
||||
|
||||
class RequestHandle {
|
||||
private:
|
||||
friend class LSPClientServer;
|
||||
LSPClientServer* mServer;
|
||||
int mId = 0;
|
||||
LSPClientServer* server;
|
||||
int id = 0;
|
||||
|
||||
public:
|
||||
RequestHandle& cancel() {
|
||||
if ( mServer )
|
||||
mServer->cancel( mId );
|
||||
if ( server )
|
||||
server->cancel( id );
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
@@ -60,51 +63,45 @@ class LSPClientServer {
|
||||
|
||||
const std::shared_ptr<ThreadPool>& getThreadPool() const;
|
||||
|
||||
LSPClientServer::RequestHandle cancel( int id );
|
||||
RequestHandle cancel( int id );
|
||||
|
||||
LSPClientServer::RequestHandle send( const json& msg, const JsonReplyHandler& h = nullptr,
|
||||
const JsonReplyHandler& eh = nullptr );
|
||||
RequestHandle send( const json& msg, const JsonReplyHandler& h = nullptr,
|
||||
const JsonReplyHandler& eh = nullptr );
|
||||
|
||||
const LSPDefinition& getDefinition() const { return mLSP; }
|
||||
|
||||
LSPClientServer::RequestHandle documentSymbols( const URI& document, const JsonReplyHandler& h,
|
||||
const JsonReplyHandler& eh );
|
||||
RequestHandle documentSymbols( const URI& document, const JsonReplyHandler& h,
|
||||
const JsonReplyHandler& eh );
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
documentSymbols( const URI& document, const ReplyHandler<std::vector<LSPSymbolInformation>>& h,
|
||||
const ReplyHandler<LSPResponseError>& eh );
|
||||
RequestHandle documentSymbols( const URI& document,
|
||||
const ReplyHandler<std::vector<LSPSymbolInformation>>& h,
|
||||
const ReplyHandler<LSPResponseError>& eh );
|
||||
|
||||
LSPClientServer::RequestHandle didOpen( const URI& document, const std::string& text,
|
||||
int version );
|
||||
RequestHandle didOpen( const URI& document, const std::string& text, int version );
|
||||
|
||||
LSPClientServer::RequestHandle didOpen( TextDocument* doc, int version );
|
||||
RequestHandle didOpen( TextDocument* doc, int version );
|
||||
|
||||
LSPClientServer::RequestHandle didSave( const URI& document, const std::string& text );
|
||||
RequestHandle didSave( const URI& document, const std::string& text );
|
||||
|
||||
LSPClientServer::RequestHandle didSave( TextDocument* doc );
|
||||
RequestHandle didSave( TextDocument* doc );
|
||||
|
||||
LSPClientServer::RequestHandle didClose( const URI& document );
|
||||
RequestHandle didClose( const URI& document );
|
||||
|
||||
LSPClientServer::RequestHandle didClose( TextDocument* document );
|
||||
RequestHandle didClose( TextDocument* document );
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
didChange( const URI& document, int version, const std::string& text,
|
||||
const std::vector<DocumentContentChange>& change = {} );
|
||||
RequestHandle didChange( const URI& document, int version, const std::string& text,
|
||||
const std::vector<DocumentContentChange>& change = {} );
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
didChange( TextDocument* doc, const std::vector<DocumentContentChange>& change = {} );
|
||||
RequestHandle didChange( TextDocument* doc,
|
||||
const std::vector<DocumentContentChange>& change = {} );
|
||||
|
||||
LSPClientServer::RequestHandle documentDefinition( const URI& document,
|
||||
const TextPosition& pos );
|
||||
RequestHandle documentDefinition( const URI& document, const TextPosition& pos );
|
||||
|
||||
LSPClientServer::RequestHandle documentDeclaration( const URI& document,
|
||||
const TextPosition& pos );
|
||||
RequestHandle documentDeclaration( const URI& document, const TextPosition& pos );
|
||||
|
||||
LSPClientServer::RequestHandle documentTypeDefinition( const URI& document,
|
||||
const TextPosition& pos );
|
||||
RequestHandle documentTypeDefinition( const URI& document, const TextPosition& pos );
|
||||
|
||||
LSPClientServer::RequestHandle documentImplementation( const URI& document,
|
||||
const TextPosition& pos );
|
||||
RequestHandle documentImplementation( const URI& document, const TextPosition& pos );
|
||||
|
||||
void updateDirty();
|
||||
|
||||
@@ -112,42 +109,54 @@ class LSPClientServer {
|
||||
|
||||
bool hasDocuments() const;
|
||||
|
||||
LSPClientServer::RequestHandle
|
||||
didChangeWorkspaceFolders( const std::vector<LSPWorkspaceFolder>& added,
|
||||
const std::vector<LSPWorkspaceFolder>& removed );
|
||||
RequestHandle didChangeWorkspaceFolders( const std::vector<LSPWorkspaceFolder>& added,
|
||||
const std::vector<LSPWorkspaceFolder>& removed );
|
||||
|
||||
void publishDiagnostics( const json& msg );
|
||||
|
||||
void workDoneProgress( const LSPWorkDoneProgressParams& workDoneParams );
|
||||
|
||||
LSPClientServer::RequestHandle getAndGoToLocation( const URI& document, const TextPosition& pos,
|
||||
const std::string& search );
|
||||
RequestHandle getAndGoToLocation( const URI& document, const TextPosition& pos,
|
||||
const std::string& search );
|
||||
|
||||
LSPClientServer::RequestHandle switchSourceHeader( const URI& document );
|
||||
RequestHandle switchSourceHeader( const URI& document );
|
||||
|
||||
LSPClientServer::RequestHandle 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 JsonReplyHandler& h );
|
||||
|
||||
LSPClientServer::RequestHandle documentCodeAction( const URI& document, const TextRange& range,
|
||||
const std::vector<std::string>& kinds,
|
||||
std::vector<LSPDiagnostic> diagnostics,
|
||||
const CodeActionHandler& h );
|
||||
RequestHandle documentCodeAction( const URI& document, const TextRange& range,
|
||||
const std::vector<std::string>& kinds,
|
||||
std::vector<LSPDiagnostic> diagnostics,
|
||||
const CodeActionHandler& h );
|
||||
|
||||
LSPClientServer::RequestHandle documentHover( const URI& document, const TextPosition& pos,
|
||||
const JsonReplyHandler& h );
|
||||
RequestHandle documentHover( const URI& document, const TextPosition& pos,
|
||||
const JsonReplyHandler& h );
|
||||
|
||||
LSPClientServer::RequestHandle documentHover( const URI& document, const TextPosition& pos,
|
||||
const HoverHandler& h );
|
||||
RequestHandle documentHover( const URI& document, const TextPosition& pos,
|
||||
const HoverHandler& h );
|
||||
|
||||
LSPClientServer::RequestHandle documentHover( TextDocument* doc, const HoverHandler& h );
|
||||
RequestHandle documentCompletion( const URI& document, const TextPosition& pos,
|
||||
const JsonReplyHandler& h );
|
||||
|
||||
LSPClientServer::RequestHandle documentCompletion( const URI& document, const TextPosition& pos,
|
||||
const JsonReplyHandler& h );
|
||||
RequestHandle documentCompletion( const URI& document, const TextPosition& pos,
|
||||
const CompletionHandler& h );
|
||||
|
||||
LSPClientServer::RequestHandle documentCompletion( const URI& document, const TextPosition& pos,
|
||||
const CompletionHandler& h );
|
||||
RequestHandle workspaceSymbol( const std::string& querySymbol, const JsonReplyHandler& h );
|
||||
|
||||
RequestHandle workspaceSymbol( const std::string& querySymbol,
|
||||
const SymbolInformationHandler& h );
|
||||
|
||||
RequestHandle 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 );
|
||||
|
||||
RequestHandle documentSemanticTokensFull( const URI& document, bool delta,
|
||||
const std::string& requestId, const TextRange& range,
|
||||
const JsonReplyHandler& h );
|
||||
|
||||
protected:
|
||||
LSPClientServerManager* mManager{ nullptr };
|
||||
@@ -177,8 +186,8 @@ class LSPClientServer {
|
||||
|
||||
void readStdErr( const char* bytes, size_t n );
|
||||
|
||||
LSPClientServer::RequestHandle write( const json& msg, const JsonReplyHandler& h = nullptr,
|
||||
const JsonReplyHandler& eh = nullptr, const int id = 0 );
|
||||
RequestHandle write( const json& msg, const JsonReplyHandler& h = nullptr,
|
||||
const JsonReplyHandler& eh = nullptr, const int id = 0 );
|
||||
|
||||
void initialize();
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ LSPClientServerManager::LSPClientServerManager() {}
|
||||
void LSPClientServerManager::load( LSPClientPlugin* plugin, const PluginManager* pluginManager,
|
||||
std::vector<LSPDefinition>&& lsps ) {
|
||||
mPlugin = plugin;
|
||||
mPluginManager = pluginManager;
|
||||
mThreadPool = pluginManager->getThreadPool();
|
||||
mLSPs = lsps;
|
||||
}
|
||||
@@ -168,6 +169,14 @@ void LSPClientServerManager::getAndGoToLocation( const std::shared_ptr<TextDocum
|
||||
server->getAndGoToLocation( doc->getURI(), doc->getSelection().start(), search );
|
||||
}
|
||||
|
||||
const PluginManager* LSPClientServerManager::getPluginManager() const {
|
||||
return mPluginManager;
|
||||
}
|
||||
|
||||
LSPClientPlugin* LSPClientServerManager::getPlugin() const {
|
||||
return mPlugin;
|
||||
}
|
||||
|
||||
void LSPClientServerManager::didChangeWorkspaceFolders( const std::string& folder ) {
|
||||
mLSPWorkspaceFolder = { "file://" + folder, FileSystem::fileNameFromPath( folder ) };
|
||||
Lock l( mClientsMutex );
|
||||
|
||||
@@ -47,9 +47,13 @@ class LSPClientServerManager {
|
||||
|
||||
void getAndGoToLocation( const std::shared_ptr<TextDocument>& doc, const std::string& search );
|
||||
|
||||
const PluginManager* getPluginManager() const;
|
||||
|
||||
LSPClientPlugin* getPlugin() const;
|
||||
|
||||
protected:
|
||||
friend class LSPClientServer;
|
||||
|
||||
const PluginManager* mPluginManager{ nullptr };
|
||||
LSPClientPlugin* mPlugin{ nullptr };
|
||||
std::shared_ptr<ThreadPool> mThreadPool;
|
||||
std::map<String::HashType, std::unique_ptr<LSPClientServer>> mClients;
|
||||
|
||||
@@ -10,8 +10,10 @@ struct LSPDefinition {
|
||||
std::string name;
|
||||
std::vector<std::string> filePatterns;
|
||||
std::string command;
|
||||
std::string commandParameters;
|
||||
std::vector<std::string> rootIndicationFileNames;
|
||||
std::string url;
|
||||
bool disabled{ false };
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -9,6 +9,9 @@ using namespace EE;
|
||||
using namespace EE::UI::Doc;
|
||||
using namespace EE::Network;
|
||||
|
||||
// The LSP protocol will be consumed by any plugin, and it's not exclusive to the LSP plugin
|
||||
// The protocol should be taken as a reference on how to implement the comunication between plugins
|
||||
|
||||
namespace ecode {
|
||||
|
||||
enum class LSPErrorCode {
|
||||
@@ -219,10 +222,6 @@ enum class LSPSymbolKind {
|
||||
TypeParameter = 26,
|
||||
};
|
||||
|
||||
enum class LSPSymbolTag : Uint8 {
|
||||
Deprecated = 1,
|
||||
};
|
||||
|
||||
struct LSPSymbolInformation {
|
||||
LSPSymbolInformation() = default;
|
||||
LSPSymbolInformation( const std::string& _name, LSPSymbolKind _kind, TextRange _range,
|
||||
@@ -230,12 +229,11 @@ struct LSPSymbolInformation {
|
||||
name( _name ), detail( _detail ), kind( _kind ), range( _range ) {}
|
||||
std::string name;
|
||||
std::string detail;
|
||||
LSPSymbolKind kind;
|
||||
LSPSymbolKind kind{ LSPSymbolKind::File };
|
||||
URI url;
|
||||
TextRange range;
|
||||
TextRange selectionRange;
|
||||
double score = 0.0;
|
||||
LSPSymbolTag tags;
|
||||
std::vector<LSPSymbolInformation> children;
|
||||
};
|
||||
|
||||
@@ -259,7 +257,6 @@ struct LSPHover {
|
||||
TextRange range;
|
||||
};
|
||||
|
||||
|
||||
enum class LSPCompletionItemKind {
|
||||
Text = 1,
|
||||
Method = 2,
|
||||
@@ -300,6 +297,11 @@ struct LSPCompletionItem {
|
||||
std::vector<LSPTextEdit> additionalTextEdits;
|
||||
};
|
||||
|
||||
struct LSPSelectionRange {
|
||||
TextRange range;
|
||||
std::shared_ptr<LSPSelectionRange> parent;
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
#endif // ECODE_LSPCLIENTPROTOCOL_HPP
|
||||
@@ -104,23 +104,24 @@ const std::string& PluginManager::getWorkspaceFolder() const {
|
||||
|
||||
void PluginManager::setWorkspaceFolder( const std::string& workspaceFolder ) {
|
||||
mWorkspaceFolder = workspaceFolder;
|
||||
sendNotification( Notification::WorkspaceFolderChanged,
|
||||
json{ { "folder", mWorkspaceFolder } } );
|
||||
json data{ { "folder", mWorkspaceFolder } };
|
||||
sendNotification( NotificationType::WorkspaceFolderChanged, NotificationFormat::JSON, &data );
|
||||
}
|
||||
|
||||
void PluginManager::pushNotification( UICodeEditorPlugin* pluginWho, Notification notification,
|
||||
const nlohmann::json& json ) const {
|
||||
void PluginManager::pushNotification( UICodeEditorPlugin* pluginWho, NotificationType notification,
|
||||
NotificationFormat format, void* data ) const {
|
||||
for ( const auto& plugin : mSubscribedPlugins )
|
||||
if ( pluginWho->getId() != plugin.first )
|
||||
plugin.second( notification, json );
|
||||
plugin.second( { notification, format, data } );
|
||||
}
|
||||
|
||||
void PluginManager::subscribeNotifications(
|
||||
UICodeEditorPlugin* plugin,
|
||||
std::function<void( Notification, const nlohmann::json& )> cb ) const {
|
||||
void PluginManager::subscribeNotifications( UICodeEditorPlugin* plugin,
|
||||
std::function<void( const Notification& )> cb ) const {
|
||||
const_cast<PluginManager*>( this )->mSubscribedPlugins[plugin->getId()] = cb;
|
||||
if ( !mWorkspaceFolder.empty() )
|
||||
cb( Notification::WorkspaceFolderChanged, json{ { "folder", mWorkspaceFolder } } );
|
||||
if ( !mWorkspaceFolder.empty() ) {
|
||||
json data{ { "folder", mWorkspaceFolder } };
|
||||
cb( { NotificationType::WorkspaceFolderChanged, NotificationFormat::JSON, &data } );
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::unsubscribeNotifications( UICodeEditorPlugin* plugin ) const {
|
||||
@@ -131,10 +132,10 @@ void PluginManager::setSplitter( UICodeEditorSplitter* splitter ) {
|
||||
mSplitter = splitter;
|
||||
}
|
||||
|
||||
void PluginManager::sendNotification( const Notification& notification,
|
||||
const nlohmann::json& json ) {
|
||||
void PluginManager::sendNotification( const NotificationType& notification,
|
||||
const NotificationFormat& format, void* data ) {
|
||||
for ( const auto& plugin : mSubscribedPlugins )
|
||||
plugin.second( notification, json );
|
||||
plugin.second( { notification, format, data } );
|
||||
}
|
||||
|
||||
bool PluginManager::hasDefinition( const std::string& id ) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef ECODE_PLUGINMANAGER_HPP
|
||||
#define ECODE_PLUGINMANAGER_HPP
|
||||
|
||||
#include "lsp/lspprotocol.hpp"
|
||||
#include <eepp/ui/models/model.hpp>
|
||||
#include <eepp/ui/tools/uicodeeditorsplitter.hpp>
|
||||
#include <eepp/ui/uicodeeditor.hpp>
|
||||
@@ -57,7 +58,19 @@ struct PluginDefinition {
|
||||
|
||||
class PluginManager {
|
||||
public:
|
||||
enum Notification { WorkspaceFolderChanged };
|
||||
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 ) );
|
||||
@@ -104,12 +117,11 @@ class PluginManager {
|
||||
|
||||
void setWorkspaceFolder( const std::string& workspaceFolder );
|
||||
|
||||
void pushNotification( UICodeEditorPlugin* pluginWho, Notification,
|
||||
const nlohmann::json& ) const;
|
||||
void pushNotification( UICodeEditorPlugin* pluginWho, NotificationType, NotificationFormat,
|
||||
void* data ) const;
|
||||
|
||||
void
|
||||
subscribeNotifications( UICodeEditorPlugin* plugin,
|
||||
std::function<void( Notification, const nlohmann::json& )> cb ) const;
|
||||
void subscribeNotifications( UICodeEditorPlugin* plugin,
|
||||
std::function<void( const Notification& )> cb ) const;
|
||||
|
||||
void unsubscribeNotifications( UICodeEditorPlugin* plugin ) const;
|
||||
|
||||
@@ -123,14 +135,14 @@ class PluginManager {
|
||||
std::map<std::string, PluginDefinition> mDefinitions;
|
||||
std::shared_ptr<ThreadPool> mThreadPool;
|
||||
UICodeEditorSplitter* mSplitter{ nullptr };
|
||||
std::map<std::string, std::function<void( Notification, const nlohmann::json& )>>
|
||||
mSubscribedPlugins;
|
||||
std::map<std::string, std::function<void( const Notification& )>> mSubscribedPlugins;
|
||||
|
||||
bool hasDefinition( const std::string& id );
|
||||
|
||||
void setSplitter( UICodeEditorSplitter* splitter );
|
||||
|
||||
void sendNotification( const Notification& notification, const nlohmann::json& json = {} );
|
||||
void sendNotification( const NotificationType& notification, const NotificationFormat& format,
|
||||
void* data );
|
||||
};
|
||||
|
||||
class PluginsModel : public Model {
|
||||
|
||||
Reference in New Issue
Block a user