mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-06-04 20:46:29 +03:00
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:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -28,7 +28,7 @@ bin/eepp-*
|
||||
bin/ecode*
|
||||
ee.tag
|
||||
log.log
|
||||
external_projects.lua
|
||||
/external_projects*.lua
|
||||
*.user
|
||||
*.filters
|
||||
*.suo
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
2
src/thirdparty/efsw
vendored
2
src/thirdparty/efsw
vendored
Submodule src/thirdparty/efsw updated: 960d474c74...03e36f9f80
@@ -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 ) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 );
|
||||
} );
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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() ||
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user