ecode: Improve LSP code action implementation, now typescript-language-server will be able to recommend adding imports. Fixed a bug introduced in the previous commit, scrollTo for Y axis now moves correctly. Started implementing LSP Code Lens but I'll keep it for the moment since it's support in LSP implementations is very limited.

This commit is contained in:
Martín Lucas Golini
2023-07-07 01:01:26 -03:00
parent c6b308a118
commit ff7d8c3478
16 changed files with 302 additions and 95 deletions

2
.gitignore vendored
View File

@@ -28,7 +28,7 @@ bin/eepp-*
bin/ecode*
ee.tag
log.log
external_projects.lua
/external_projects*.lua
*.user
*.filters
*.suo

View File

@@ -1476,3 +1476,7 @@ solution "eepp"
if os.isfile("external_projects.lua") then
dofile("external_projects.lua")
end
if os.isfile("external_projects_premake4.lua") then
dofile("external_projects_premake4.lua")
end

View File

@@ -1212,3 +1212,7 @@ workspace "eepp"
if os.isfile("external_projects.lua") then
dofile("external_projects.lua")
end
if os.isfile("external_projects_premake5.lua") then
dofile("external_projects_premake5.lua")
end

View File

@@ -1694,30 +1694,30 @@ void UICodeEditor::scrollTo( TextRange position, bool centered, bool forceExactP
Int64 minDistance = mHScrollBar->isVisible() ? 3 : 2;
if ( forceExactPosition || position.start().line() <= (Int64)lineRange.first ||
position.start().line() >= (Int64)lineRange.second - minDistance ) {
if ( forceExactPosition || position.end().line() <= (Int64)lineRange.first ||
position.end().line() >= (Int64)lineRange.second - minDistance ) {
// Vertical Scroll
Float lineHeight = getLineHeight();
Float min = eefloor( lineHeight * ( eemax<Float>( 0, position.start().line() - 1 ) ) );
Float min = eefloor( lineHeight * ( eemax<Float>( 0, position.end().line() - 1 ) ) );
Float max =
eefloor( lineHeight * ( position.start().line() + minDistance ) - mSize.getHeight() );
eefloor( lineHeight * ( position.end().line() + minDistance ) - mSize.getHeight() );
Float halfScreenLines = eefloor( mSize.getHeight() / lineHeight * 0.5f );
if ( forceExactPosition ) {
setScrollY( lineHeight *
( eemax<Float>( 0, position.start().line() - 1 -
( eemax<Float>( 0, position.end().line() - 1 -
( centered ? halfScreenLines : 0 ) ) ) );
} else if ( min < mScroll.y ) {
if ( centered ) {
if ( position.start().line() - 1 - halfScreenLines >= 0 )
min = eefloor( lineHeight * ( eemax<Float>( 0, position.start().line() - 1 -
if ( position.end().line() - 1 - halfScreenLines >= 0 )
min = eefloor( lineHeight * ( eemax<Float>( 0, position.end().line() - 1 -
halfScreenLines ) ) );
}
setScrollY( min );
} else if ( max > mScroll.y ) {
if ( centered ) {
max = eefloor( lineHeight *
( position.start().line() + minDistance + halfScreenLines ) -
( position.end().line() + minDistance + halfScreenLines ) -
mSize.getHeight() );
max = eemin( max, getMaxScroll().y );
}

View File

@@ -339,6 +339,56 @@ void LinterPlugin::setMatches( TextDocument* doc, const MatchOrigin& origin,
invalidateEditors( doc );
}
// TODO: Clean up this
static json toJson( const TextPosition& pos ) {
return json{ { "line", pos.line() }, { "character", pos.column() } };
}
static json toJson( const TextRange& pos ) {
return json{ { "start", toJson( pos.start() ) }, { "end", toJson( pos.end() ) } };
}
static json toJson( const LSPLocation& location ) {
if ( !location.uri.empty() ) {
return json{ { "uri", location.uri.toString() }, { "range", toJson( location.range ) } };
}
return json();
}
static json toJson( const LSPDiagnosticRelatedInformation& related ) {
auto loc = toJson( related.location );
if ( loc.is_object() ) {
return json{ { "location", toJson( related.location ) }, { "message", related.message } };
}
return json();
}
static json toJson( const LSPDiagnostic& diagnostic ) {
// required
auto result = json();
result["range"] = toJson( diagnostic.range );
result["message"] = diagnostic.message;
// optional
if ( !diagnostic.code.empty() )
result["code"] = diagnostic.code;
if ( diagnostic.severity != LSPDiagnosticSeverity::Unknown )
result["severity"] = static_cast<int>( diagnostic.severity );
if ( !diagnostic.source.empty() )
result["source"] = diagnostic.source;
json relatedInfo;
for ( const auto& vrelated : diagnostic.relatedInformation ) {
auto related = toJson( vrelated );
if ( related.is_object() ) {
relatedInfo.push_back( related );
}
}
if ( !relatedInfo.empty() )
result["relatedInformation"] = relatedInfo;
if ( !diagnostic.data.empty() )
result["data"] = diagnostic.data;
return result;
}
PluginRequestHandle LinterPlugin::processMessage( const PluginMessage& notification ) {
if ( notification.type == PluginMessageType::FileSystemListenerReady ) {
subscribeFileSystemListener();
@@ -352,27 +402,30 @@ PluginRequestHandle LinterPlugin::processMessage( const PluginMessage& notificat
if ( doc ) {
Lock l( mMatchesMutex );
auto foundMatch = mMatches.find( doc );
if ( foundMatch != mMatches.end() ) {
auto pos = TextPosition::fromString( notification.asJSON().value( "pos", "" ) );
if ( pos.isValid() ) {
auto foundLine = foundMatch->second.find( pos.line() );
if ( foundLine != foundMatch->second.end() ) {
LSPDiagnosticsCodeAction quickFix;
for ( const auto& match : foundLine->second ) {
if ( !match.codeActions.empty() ) {
for ( const auto& ca : match.codeActions ) {
quickFix = ca;
if ( quickFix.isPreferred )
break;
}
}
}
mManager->sendResponse( this, PluginMessageType::DiagnosticsCodeAction,
PluginMessageFormat::DiagnosticsCodeAction,
&quickFix, id );
if ( foundMatch == mMatches.end() )
return {};
auto pos = TextPosition::fromString( notification.asJSON().value( "pos", "" ) );
if ( !pos.isValid() )
return {};
auto foundLine = foundMatch->second.find( pos.line() );
if ( foundLine == foundMatch->second.end() )
return {};
LSPDiagnosticsCodeAction quickFix;
for ( const auto& match : foundLine->second ) {
if ( !match.diagnostic.codeActions.empty() ) {
for ( const auto& ca : match.diagnostic.codeActions ) {
quickFix = ca;
if ( quickFix.isPreferred )
break;
}
}
}
mManager->sendResponse( this, PluginMessageType::DiagnosticsCodeAction,
PluginMessageFormat::DiagnosticsCodeAction, &quickFix, id );
}
return {};
@@ -393,7 +446,7 @@ PluginRequestHandle LinterPlugin::processMessage( const PluginMessage& notificat
if ( found == mMatches.end() )
return {};
TextPosition pos( LSPConverter::fromJSON( j ) );
TextPosition pos( LSPConverter::parsePosition( j ) );
const auto& docMatches = found->second;
@@ -420,6 +473,46 @@ PluginRequestHandle LinterPlugin::processMessage( const PluginMessage& notificat
return {};
}
if ( notification.type == PluginMessageType::GetDiagnostics &&
notification.format == PluginMessageFormat::JSON && notification.isRequest() ) {
const json& j = notification.asJSON();
if ( !j.contains( "uri" ) || !j.contains( "line" ) || !j.contains( "character" ) )
return {};
URI uri( j["uri"].get<std::string>() );
TextDocument* doc = getDocumentFromURI( uri );
if ( nullptr == doc )
return {};
Lock l( mMatchesMutex );
auto found = mMatches.find( doc );
if ( found == mMatches.end() )
return {};
TextPosition pos( LSPConverter::parsePosition( j ) );
const auto& docMatches = found->second;
auto foundLine = docMatches.find( pos.line() );
if ( foundLine == docMatches.end() )
return {};
const auto& matches = foundLine->second;
for ( const auto& match : matches ) {
if ( pos.column() >= match.range.start().column() &&
pos.column() <= match.range.end().column() ) {
PluginInmediateResponse msg;
msg.type = PluginMessageType::GetDiagnostics;
json rj;
rj["diagnostics"] = json::array( { toJson( match.diagnostic ) } );
msg.data = std::move( rj );
return PluginRequestHandle( msg );
}
}
return {};
}
if ( !mEnableLSPDiagnostics || notification.type != PluginMessageType::Diagnostics ||
notification.format != PluginMessageFormat::Diagnostics )
return PluginRequestHandle::empty();
@@ -439,8 +532,7 @@ PluginRequestHandle LinterPlugin::processMessage( const PluginMessage& notificat
match.type = getLinterTypeFromSeverity( diag.severity );
match.lineCache = doc->line( match.range.start().line() ).getHash();
match.origin = MatchOrigin::Diagnostics;
if ( !diag.codeActions.empty() )
match.codeActions = diag.codeActions;
match.diagnostic = std::move( diag );
matches[match.range.start().line()].emplace_back( std::move( match ) );
}
@@ -906,7 +998,7 @@ void LinterPlugin::drawAfterLineText( UICodeEditor* editor, const Int64& index,
Float rLineWidth = 0;
if ( !quickFixRendered && doc->getSelection().start().line() == index &&
!match.codeActions.empty() ) {
!match.diagnostic.codeActions.empty() ) {
rLineWidth = editor->getLineWidth( index );
Color wcolor( editor->getColorScheme().getEditorSyntaxStyle( "warning" ).color );
if ( nullptr == mLightbulbIcon ) {

View File

@@ -42,10 +42,10 @@ struct LinterMatch {
std::string text;
TextRange range;
LinterType type{ LinterType::Error };
String::HashType lineCache;
String::HashType lineCache{ 0 };
MatchOrigin origin{ MatchOrigin::Linter };
std::map<UICodeEditor*, Rectf> box;
std::vector<LSPDiagnosticsCodeAction> codeActions;
LSPDiagnostic diagnostic;
};
class LinterPlugin : public Plugin {

View File

@@ -184,7 +184,7 @@ LSPPositionAndServer getLSPLocationFromJSON( LSPClientServerManager& manager, co
if ( !data.contains( "uri" ) || !data.contains( "position" ) )
return {};
TextPosition position( LSPConverter::fromJSON( data["position"] ) );
TextPosition position( LSPConverter::parsePosition( data["position"] ) );
if ( !position.isValid() )
return {};
@@ -1004,19 +1004,33 @@ void LSPClientPlugin::processDiagnosticsCodeAction( const PluginMessage& msg ) {
if ( !( msg.isResponse() && msg.type == PluginMessageType::DiagnosticsCodeAction &&
msg.format == PluginMessageFormat::DiagnosticsCodeAction ) )
return;
mQuickFix = msg.asDiasnosticsCodeAction();
}
void LSPClientPlugin::codeAction( UICodeEditor* editor ) {
json j;
// It seems that we don't require this hack anymore
// TODO: Check if this is needed in some situation
/*json j;
j["uri"] = editor->getDocument().getURI().toString();
j["pos"] = editor->getDocument().getSelection().start().toString();
mQuickFix = {};
auto req = mManager->sendRequest( PluginMessageType::DiagnosticsCodeAction,
PluginMessageFormat::JSON, &j );
PluginMessageFormat::JSON, &j );*/
json j2;
j2["uri"] = editor->getDocument().getURI().toString();
j2["line"] = editor->getDocument().getSelection().start().line();
j2["character"] = editor->getDocument().getSelection().start().column();
auto resp =
mManager->sendRequest( PluginMessageType::GetDiagnostics, PluginMessageFormat::JSON, &j2 );
nlohmann::json diagnostics;
if ( resp.isResponse() )
diagnostics = std::move( resp.getResponse().data );
mClientManager.codeAction(
editor->getDocumentRef(),
editor->getDocumentRef(), diagnostics,
[&, editor]( const LSPClientServer::IdType&, const std::vector<LSPCodeAction>& res ) {
createCodeActionsView( editor, res );
} );

View File

@@ -289,6 +289,12 @@ static void fromJson( LSPWorkspaceFoldersServerCapabilities& options, const json
}
}
static void fromJson( LSPCodeLensOptions& options, const json& json ) {
options.supported = true;
if ( json.is_object() )
options.resolveProvider = json.value( "resolveProvider", false );
}
static void fromJson( LSPSemanticTokensOptions& options, const json& data ) {
if ( data.empty() )
return;
@@ -373,6 +379,8 @@ static void fromJson( LSPServerCapabilities& caps, const json& json ) {
fromJson( caps.workspaceFolders, workspace["workspaceFolders"] );
}
caps.selectionRangeProvider = toBoolOrObject( json, "selectionRangeProvider" );
if ( json.contains( "codeLensProvider" ) )
fromJson( caps.codeLensProvider, json["codeLensProvider"] );
caps.ready = true;
}
@@ -586,7 +594,17 @@ static std::vector<LSPDiagnostic> parseDiagnostics( const json& result ) {
relatedInfoList.push_back( { relLocation, relMessage } );
}
}
ret.push_back( { range, severity, code, source, message, relatedInfoList, {} } );
json data;
if ( diag.contains( "data" ) )
data = diag["data"];
ret.push_back( { std::move( range ),
std::move( severity ),
std::move( code ),
std::move( source ),
std::move( message ),
std::move( relatedInfoList ),
{},
std::move( data ) } );
}
return ret;
}
@@ -640,59 +658,39 @@ static std::vector<LSPDiagnosticsCodeAction> parseDiagnosticsCodeAction( const j
return ret;
}
static json toJson( const LSPLocation& location ) {
if ( !location.uri.empty() ) {
return json{ { MEMBER_URI, location.uri.toString() },
{ MEMBER_RANGE, toJson( location.range ) } };
}
return json();
}
static std::vector<LSPCodeLens> parseCodeLens( const json& result ) {
if ( result.empty() || !result.is_array() )
return {};
static json toJson( const LSPDiagnosticRelatedInformation& related ) {
auto loc = toJson( related.location );
if ( loc.is_object() ) {
return json{ { MEMBER_LOCATION, toJson( related.location ) },
{ MEMBER_MESSAGE, related.message } };
}
return json();
}
std::vector<LSPCodeLens> codeLens;
static json toJson( const LSPDiagnostic& diagnostic ) {
// required
auto result = json();
result[MEMBER_RANGE] = toJson( diagnostic.range );
result[MEMBER_MESSAGE] = diagnostic.message;
// optional
if ( !diagnostic.code.empty() )
result[( "code" )] = diagnostic.code;
if ( diagnostic.severity != LSPDiagnosticSeverity::Unknown )
result[( "severity" )] = static_cast<int>( diagnostic.severity );
if ( !diagnostic.source.empty() )
result[( "source" )] = diagnostic.source;
json relatedInfo;
for ( const auto& vrelated : diagnostic.relatedInformation ) {
auto related = toJson( vrelated );
if ( related.is_object() ) {
relatedInfo.push_back( related );
}
for ( const auto& codeLen : result ) {
if ( !codeLen.contains( "range" ) )
continue;
LSPCodeLens cl;
cl.range = parseRange( codeLen["range"] );
if ( codeLen.contains( "command" ) )
cl.command = parseCommand( codeLen["command"] );
if ( codeLen.contains( "data" ) )
cl.data = codeLen["data"];
codeLens.emplace_back( cl );
}
result[( "relatedInformation" )] = relatedInfo;
return result;
return codeLens;
}
static json codeActionParams( const URI& document, const TextRange& range,
const std::vector<std::string>& kinds,
const std::vector<LSPDiagnostic>& diagnostics ) {
const std::vector<std::string>& kinds, const json& diagnostics ) {
auto params = textDocumentParams( document );
params[MEMBER_RANGE] = toJson( range );
json context;
json diags = json::array();
for ( const auto& diagnostic : diagnostics )
diags.push_back( toJson( diagnostic ) );
context[MEMBER_DIAGNOSTICS] = diags;
if ( !kinds.empty() )
context["only"] = json( kinds );
params["context"] = context;
if ( !diagnostics.empty() )
params["context"] = diagnostics;
return params;
}
@@ -763,7 +761,12 @@ static std::vector<LSPDiagnostic> parseDiagnosticsArr( const json& result ) {
std::vector<LSPDiagnosticsCodeAction> codeActions;
if ( diag.contains( "codeActions" ) )
codeActions = parseDiagnosticsCodeAction( diag["codeActions"] );
ret.push_back( { range, severity, code, source, message, relatedInfoList, codeActions } );
nlohmann::json data;
if ( diag.contains( "data" ) )
data = diag["data"];
ret.push_back( { std::move( range ), std::move( severity ), std::move( code ),
std::move( source ), std::move( message ), std::move( relatedInfoList ),
std::move( codeActions ), std::move( data ) } );
}
return ret;
}
@@ -990,21 +993,30 @@ void LSPClientServer::registerCapabilities( const json& jcap ) {
for ( const auto& reg : registrations ) {
if ( reg.contains( "method" ) ) {
if ( reg["method"] == "workspace/executeCommand" ) {
const auto& method = reg["method"];
if ( method == "workspace/executeCommand" ) {
mCapabilities.executeCommandProvider = true;
registered = true;
} else if ( reg["method"] == "textDocument/documentSymbol" ) {
} else if ( method == "textDocument/documentSymbol" ) {
mCapabilities.documentSymbolProvider = true;
registered = true;
} else if ( reg["method"] == "textDocument/rename" ) {
} else if ( method == "textDocument/rename" ) {
mCapabilities.renameProvider = true;
registered = true;
} else if ( reg["method"] == "textDocument/formatting" ) {
} else if ( method == "textDocument/formatting" ) {
mCapabilities.documentFormattingProvider = true;
registered = true;
} else if ( reg["method"] == "textDocument/rangeFormatting" ) {
} else if ( method == "textDocument/rangeFormatting" ) {
mCapabilities.documentRangeFormattingProvider = true;
registered = true;
} else if ( method == "textDocument/codeLens" ) {
mCapabilities.codeLensProvider.supported = true;
if ( reg.contains( "registerOptions" ) )
fromJson( mCapabilities.codeLensProvider, reg["registerOptions"] );
registered = true;
} else if ( method == "textDocument/codeAction" ) {
mCapabilities.codeActionProvider = true;
registered = true;
}
}
}
@@ -1023,6 +1035,9 @@ void LSPClientServer::initialize() {
{ "codeActionLiteralSupport",
json{ { "codeActionKind", json{ { "valueSet", json::array( { "quickfix", "refactor",
"source" } ) } } } } } };
codeAction["dataSupport"] = true;
codeAction["isPreferredSupport"] = true;
codeAction["dynamicRegistration"] = true;
json semanticTokens{
{ "requests", json{ { "range", true }, { "full", json{ { "delta", true } } } } },
@@ -1044,6 +1059,7 @@ void LSPClientServer::initialize() {
workspace["executeCommand"] = json{ { "dynamicRegistration", true } };
workspace["workspaceFolders"] = true;
workspace["semanticTokens"] = json{ { "refreshSupport", true } };
workspace["codeLens"] = json{ { "refreshSupport", true } };
json capabilities{
{ "textDocument",
@@ -1061,6 +1077,7 @@ void LSPClientServer::initialize() {
{ "rename", json{ { "dynamicRegistration", true } } },
{ "formatting", json{ { "dynamicRegistration", true } } },
{ "rangeFormatting", json{ { "dynamicRegistration", true } } },
{ "codeLens", json{ { "dynamicRegistration", true } } },
} },
{ "window", json{ { "workDoneProgress", true },
{ "showMessage", showMessage },
@@ -1360,6 +1377,12 @@ void LSPClientServer::refreshSmenaticHighlighting() {
}
}
void LSPClientServer::refreshCodeLens() {
Lock l( mClientsMutex );
for ( const auto& client : mClients )
client.second->requestCodeLens();
}
LSPClientServer::LSPRequestHandle LSPClientServer::didOpen( const URI& document,
const std::string& text, int version ) {
auto params = textDocumentParams( textDocumentItem( document, mLSP.language, text, version ) );
@@ -1631,6 +1654,10 @@ void LSPClientServer::processRequest( const json& msg ) {
refreshSmenaticHighlighting();
write( newEmptyResult( msgid ) );
return;
} else if ( method == "workspace/codeLens/refresh" ) {
refreshCodeLens();
write( newEmptyResult( msgid ) );
return;
} else if ( method == "client/registerCapability" ) {
registerCapabilities( msg[MEMBER_PARAMS] );
write( newEmptyResult( msgid ) );
@@ -1890,15 +1917,15 @@ LSPClientServer::didChangeWorkspaceFolders( const std::vector<LSPWorkspaceFolder
void LSPClientServer::documentCodeAction( const URI& document, const TextRange& range,
const std::vector<std::string>& kinds,
std::vector<LSPDiagnostic> diagnostics,
const nlohmann::json& diagnostics,
const JsonReplyHandler& h ) {
auto params = codeActionParams( document, range, kinds, std::move( diagnostics ) );
auto params = codeActionParams( document, range, kinds, diagnostics );
sendAsync( newRequest( "textDocument/codeAction", params ), h );
}
void LSPClientServer::documentCodeAction( const URI& document, const TextRange& range,
const std::vector<std::string>& kinds,
std::vector<LSPDiagnostic> diagnostics,
const nlohmann::json& diagnostics,
const CodeActionHandler& h ) {
documentCodeAction( document, range, kinds, diagnostics,
[h]( const IdType& id, const json& json ) {
@@ -1907,6 +1934,17 @@ void LSPClientServer::documentCodeAction( const URI& document, const TextRange&
} );
}
void LSPClientServer::documentCodeLens( const URI& document, const JsonReplyHandler& h ) {
sendAsync( newRequest( "textDocument/codeLens", textDocumentParams( document ) ), h );
}
void LSPClientServer::documentCodeLens( const URI& document, const CodeLensHandler& h ) {
documentCodeLens( document, [h]( const IdType& id, const json& json ) {
if ( h )
h( id, parseCodeLens( json ) );
} );
}
void LSPClientServer::documentHover( const URI& document, const TextPosition& pos,
const JsonReplyHandler& h ) {
auto params = textDocumentPositionParams( document, pos );

View File

@@ -38,6 +38,7 @@ class LSPClientServer {
using JsonReplyHandler = ReplyHandler<json>;
using CodeActionHandler = ReplyHandler<std::vector<LSPCodeAction>>;
using CodeLensHandler = ReplyHandler<std::vector<LSPCodeLens>>;
using HoverHandler = ReplyHandler<LSPHover>;
using CompletionHandler = ReplyHandler<LSPCompletionList>;
using SymbolInformationHandler = WReplyHandler<LSPSymbolInformationList>;
@@ -150,11 +151,15 @@ class LSPClientServer {
void documentCodeAction( const URI& document, const TextRange& range,
const std::vector<std::string>& kinds,
std::vector<LSPDiagnostic> diagnostics, const JsonReplyHandler& h );
const nlohmann::json& diagnostics, const JsonReplyHandler& h );
void documentCodeAction( const URI& document, const TextRange& range,
const std::vector<std::string>& kinds,
std::vector<LSPDiagnostic> diagnostics, const CodeActionHandler& h );
const nlohmann::json& diagnostics, const CodeActionHandler& h );
void documentCodeLens( const URI& document, const JsonReplyHandler& h );
void documentCodeLens( const URI& document, const CodeLensHandler& h );
void documentHover( const URI& document, const TextPosition& pos, const JsonReplyHandler& h );
@@ -300,6 +305,8 @@ class LSPClientServer {
const JsonReplyHandler& eh = nullptr );
void refreshSmenaticHighlighting();
void refreshCodeLens();
};
} // namespace ecode

View File

@@ -436,18 +436,23 @@ void LSPClientServerManager::getSymbolReferences( std::shared_ptr<TextDocument>
}
void LSPClientServerManager::codeAction( std::shared_ptr<TextDocument> doc,
const nlohmann::json& diagnostics,
const LSPClientServer::CodeActionHandler& h ) {
auto* server = getOneLSPClientServer( doc );
if ( !server )
return;
auto range = doc->getSelection();
if ( !doc->hasSelection() ) {
if ( !diagnostics.empty() && diagnostics.contains( "diagnostics" ) &&
diagnostics["diagnostics"].is_array() && !diagnostics["diagnostics"].empty() &&
diagnostics["diagnostics"][0].contains( "range" ) ) {
range = LSPConverter::parseRange( diagnostics["diagnostics"][0]["range"] );
} else if ( !doc->hasSelection() ) {
range = { doc->startOfLine( range.start() ),
doc->positionOffset( doc->endOfLine( range.end() ), 1 ) };
}
server->documentCodeAction( doc->getURI(), range, {}, {}, h );
server->documentCodeAction( doc->getURI(), range, {}, diagnostics, h );
}
void LSPClientServerManager::memoryUsage( std::shared_ptr<TextDocument> doc ) {

View File

@@ -71,7 +71,7 @@ class LSPClientServerManager {
void getSymbolReferences( std::shared_ptr<TextDocument> doc );
void codeAction( std::shared_ptr<TextDocument> doc,
void codeAction( std::shared_ptr<TextDocument> doc, const nlohmann::json& diagnostics,
const LSPClientServer::CodeActionHandler& h );
void memoryUsage( std::shared_ptr<TextDocument> doc );
@@ -99,6 +99,7 @@ class LSPClientServerManager {
void requestSymanticHighlighting( std::shared_ptr<TextDocument> doc );
void rangeFormatting( std::shared_ptr<TextDocument> doc );
protected:
friend class LSPClientServer;
PluginManager* mPluginManager{ nullptr };

View File

@@ -33,6 +33,7 @@ LSPDocumentClient::~LSPDocumentClient() {
void LSPDocumentClient::onDocumentLoaded( TextDocument* ) {
requestSemanticHighlightingDelayed();
// requestCodeLens();
}
void LSPDocumentClient::onDocumentTextChanged( const DocumentContentChange& change ) {
@@ -104,6 +105,7 @@ int LSPDocumentClient::getVersion() const {
void LSPDocumentClient::onServerInitialized() {
requestSymbols();
requestSemanticHighlighting();
// requestCodeLens();
}
void LSPDocumentClient::refreshTag() {
@@ -178,6 +180,21 @@ bool LSPDocumentClient::isWaitingSemanticTokensResponse() const {
return mWaitingSemanticTokensResponse;
}
void LSPDocumentClient::requestCodeLens() {
if ( !mServer || !mServer->getCapabilities().codeLensProvider.supported )
return;
URI uri = mDoc->getURI();
LSPClientServer* server = mServer;
LSPDocumentClient* docClient = this;
mServer->documentCodeLens(
uri, [uri, server, docClient]( const auto&, const std::vector<LSPCodeLens>& codeLens ) {
if ( server->hasDocument( uri ) ) {
docClient->mCodeLens = codeLens;
}
} );
}
UISceneNode* LSPDocumentClient::getUISceneNode() {
LSPClientServer* server = mServer;
if ( !server || !server->getManager() || !server->getManager()->getPluginManager() ||

View File

@@ -61,6 +61,8 @@ class LSPDocumentClient : public TextDocument::Client {
bool isWaitingSemanticTokensResponse() const;
void requestCodeLens();
protected:
LSPClientServer* mServer{ nullptr };
LSPClientServerManager* mServerManager{ nullptr };
@@ -70,6 +72,7 @@ class LSPDocumentClient : public TextDocument::Client {
int mVersion{ 0 };
std::string mSemanticeResultId;
LSPSemanticTokensDelta mSemanticTokens;
std::vector<LSPCodeLens> mCodeLens;
bool mRunningSemanticTokens{ false };
bool mWaitingSemanticTokensResponse{ false };
bool mShutdown{ false };

View File

@@ -128,6 +128,11 @@ struct LSPWorkspaceFoldersServerCapabilities {
bool changeNotifications = false;
};
struct LSPCodeLensOptions {
bool supported = false;
bool resolveProvider = false;
};
struct LSPServerCapabilities {
bool ready = false;
std::vector<std::string> languages;
@@ -146,6 +151,7 @@ struct LSPServerCapabilities {
bool documentHighlightProvider = false;
bool documentFormattingProvider = false;
bool documentRangeFormattingProvider = false;
LSPCodeLensOptions codeLensProvider;
bool workspaceSymbolProvider = false;
LSPDocumentOnTypeFormattingOptions documentOnTypeFormattingProvider;
bool renameProvider = false;
@@ -217,12 +223,13 @@ struct LSPDiagnosticsCodeAction {
struct LSPDiagnostic {
TextRange range;
LSPDiagnosticSeverity severity;
LSPDiagnosticSeverity severity{ LSPDiagnosticSeverity::Unknown };
std::string code;
std::string source;
std::string message;
std::vector<LSPDiagnosticRelatedInformation> relatedInformation;
std::vector<LSPDiagnosticsCodeAction> codeActions;
nlohmann::json data;
};
struct LSPPublishDiagnosticsParams {
@@ -246,6 +253,12 @@ struct LSPCodeAction {
bool isPreferred{ false };
};
struct LSPCodeLens {
TextRange range;
LSPCommand command;
nlohmann::json data;
};
enum class LSPWorkDoneProgressKind { Begin, Report, End };
struct LSPWorkDoneProgressValue {
@@ -544,11 +557,19 @@ struct LSPSignatureHelp {
};
struct LSPConverter {
static TextPosition fromJSON( const nlohmann::json& data ) {
static TextPosition parsePosition( const nlohmann::json& data ) {
if ( data.contains( "line" ) && data.contains( "character" ) )
return { data["line"].get<Int64>(), data["character"].get<Int64>() };
return {};
}
static TextRange parseRange( const nlohmann::json& range ) {
if ( !range.contains( "start" ) || !range.contains( "end" ) )
return {};
auto startpos = parsePosition( range["start"] );
auto endpos = parsePosition( range["end"] );
return { startpos, endpos };
}
};
struct LSPShowDocumentParams {

View File

@@ -83,6 +83,7 @@ enum class PluginMessageType {
// available
GetErrorOrWarning, // Request a component to provide the information of an error or warning in a
// particular document location
GetDiagnostics, // Request the diagnostic information from a cursor position
Undefined
};