mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
LSP: Implement semantic tokens
This commit is contained in:
parent
f629f9eff0
commit
ca3af4b2a2
@ -4,6 +4,7 @@ Language Features:
|
|||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
* LSP: Add rudimentary support for semantic highlighting.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -163,6 +163,8 @@ set(sources
|
|||||||
lsp/HandlerBase.h
|
lsp/HandlerBase.h
|
||||||
lsp/LanguageServer.cpp
|
lsp/LanguageServer.cpp
|
||||||
lsp/LanguageServer.h
|
lsp/LanguageServer.h
|
||||||
|
lsp/SemanticTokensBuilder.cpp
|
||||||
|
lsp/SemanticTokensBuilder.h
|
||||||
lsp/Transport.cpp
|
lsp/Transport.cpp
|
||||||
lsp/Transport.h
|
lsp/Transport.h
|
||||||
lsp/Utils.cpp
|
lsp/Utils.cpp
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
// LSP feature implementations
|
// LSP feature implementations
|
||||||
#include <libsolidity/lsp/GotoDefinition.h>
|
#include <libsolidity/lsp/GotoDefinition.h>
|
||||||
|
#include <libsolidity/lsp/SemanticTokensBuilder.h>
|
||||||
|
|
||||||
#include <liblangutil/SourceReferenceExtractor.h>
|
#include <liblangutil/SourceReferenceExtractor.h>
|
||||||
#include <liblangutil/CharStream.h>
|
#include <liblangutil/CharStream.h>
|
||||||
@ -64,6 +65,49 @@ int toDiagnosticSeverity(Error::Type _errorType)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Json::Value semanticTokensLegend()
|
||||||
|
{
|
||||||
|
Json::Value legend = Json::objectValue;
|
||||||
|
|
||||||
|
// NOTE! The (alphabetical) order and items must match exactly the items of
|
||||||
|
// their respective enum class members.
|
||||||
|
|
||||||
|
Json::Value tokenTypes = Json::arrayValue;
|
||||||
|
tokenTypes.append("class");
|
||||||
|
tokenTypes.append("comment");
|
||||||
|
tokenTypes.append("enum");
|
||||||
|
tokenTypes.append("enumMember");
|
||||||
|
tokenTypes.append("event");
|
||||||
|
tokenTypes.append("function");
|
||||||
|
tokenTypes.append("interface");
|
||||||
|
tokenTypes.append("keyword");
|
||||||
|
tokenTypes.append("macro");
|
||||||
|
tokenTypes.append("method");
|
||||||
|
tokenTypes.append("modifier");
|
||||||
|
tokenTypes.append("number");
|
||||||
|
tokenTypes.append("operator");
|
||||||
|
tokenTypes.append("parameter");
|
||||||
|
tokenTypes.append("property");
|
||||||
|
tokenTypes.append("string");
|
||||||
|
tokenTypes.append("struct");
|
||||||
|
tokenTypes.append("type");
|
||||||
|
tokenTypes.append("typeParameter");
|
||||||
|
tokenTypes.append("variable");
|
||||||
|
legend["tokenTypes"] = tokenTypes;
|
||||||
|
|
||||||
|
Json::Value tokenModifiers = Json::arrayValue;
|
||||||
|
tokenModifiers.append("abstract");
|
||||||
|
tokenModifiers.append("declaration");
|
||||||
|
tokenModifiers.append("definition");
|
||||||
|
tokenModifiers.append("deprecated");
|
||||||
|
tokenModifiers.append("documentation");
|
||||||
|
tokenModifiers.append("modification");
|
||||||
|
tokenModifiers.append("readonly");
|
||||||
|
legend["tokenModifiers"] = tokenModifiers;
|
||||||
|
|
||||||
|
return legend;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LanguageServer::LanguageServer(Transport& _transport):
|
LanguageServer::LanguageServer(Transport& _transport):
|
||||||
@ -81,6 +125,7 @@ LanguageServer::LanguageServer(Transport& _transport):
|
|||||||
{"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _2)},
|
{"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _2)},
|
||||||
{"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _2)},
|
{"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _2)},
|
||||||
{"textDocument/implementation", GotoDefinition(*this) },
|
{"textDocument/implementation", GotoDefinition(*this) },
|
||||||
|
{"textDocument/semanticTokens/full", bind(&LanguageServer::semanticTokensFull, this, _1, _2)},
|
||||||
{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _2)},
|
{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _2)},
|
||||||
},
|
},
|
||||||
m_fileRepository("/" /* basePath */),
|
m_fileRepository("/" /* basePath */),
|
||||||
@ -266,10 +311,30 @@ void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
|
|||||||
replyArgs["capabilities"]["implementationProvider"] = true;
|
replyArgs["capabilities"]["implementationProvider"] = true;
|
||||||
replyArgs["capabilities"]["textDocumentSync"]["change"] = 2; // 0=none, 1=full, 2=incremental
|
replyArgs["capabilities"]["textDocumentSync"]["change"] = 2; // 0=none, 1=full, 2=incremental
|
||||||
replyArgs["capabilities"]["textDocumentSync"]["openClose"] = true;
|
replyArgs["capabilities"]["textDocumentSync"]["openClose"] = true;
|
||||||
|
replyArgs["capabilities"]["semanticTokensProvider"]["legend"] = semanticTokensLegend();
|
||||||
|
replyArgs["capabilities"]["semanticTokensProvider"]["range"] = false;
|
||||||
|
replyArgs["capabilities"]["semanticTokensProvider"]["full"] = true; // XOR requests.full.delta = true
|
||||||
|
|
||||||
m_client.reply(_id, move(replyArgs));
|
m_client.reply(_id, move(replyArgs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LanguageServer::semanticTokensFull(MessageID _id, Json::Value const& _args)
|
||||||
|
{
|
||||||
|
auto uri = _args["textDocument"]["uri"];
|
||||||
|
|
||||||
|
compile();
|
||||||
|
|
||||||
|
auto const sourceName = m_fileRepository.uriToSourceUnitName(uri.as<string>());
|
||||||
|
SourceUnit const& ast = m_compilerStack.ast(sourceName);
|
||||||
|
m_compilerStack.charStream(sourceName);
|
||||||
|
Json::Value data = SemanticTokensBuilder().build(ast, m_compilerStack.charStream(sourceName));
|
||||||
|
|
||||||
|
Json::Value reply = Json::objectValue;
|
||||||
|
reply["data"] = data;
|
||||||
|
|
||||||
|
m_client.reply(_id, std::move(reply));
|
||||||
|
}
|
||||||
|
|
||||||
void LanguageServer::handleWorkspaceDidChangeConfiguration(Json::Value const& _args)
|
void LanguageServer::handleWorkspaceDidChangeConfiguration(Json::Value const& _args)
|
||||||
{
|
{
|
||||||
requireServerInitialized();
|
requireServerInitialized();
|
||||||
|
@ -73,6 +73,7 @@ private:
|
|||||||
void handleTextDocumentDidChange(Json::Value const& _args);
|
void handleTextDocumentDidChange(Json::Value const& _args);
|
||||||
void handleTextDocumentDidClose(Json::Value const& _args);
|
void handleTextDocumentDidClose(Json::Value const& _args);
|
||||||
void handleGotoDefinition(MessageID _id, Json::Value const& _args);
|
void handleGotoDefinition(MessageID _id, Json::Value const& _args);
|
||||||
|
void semanticTokensFull(MessageID _id, Json::Value const& _args);
|
||||||
|
|
||||||
/// Invoked when the server user-supplied configuration changes (initiated by the client).
|
/// Invoked when the server user-supplied configuration changes (initiated by the client).
|
||||||
void changeConfiguration(Json::Value const&);
|
void changeConfiguration(Json::Value const&);
|
||||||
|
283
libsolidity/lsp/SemanticTokensBuilder.cpp
Normal file
283
libsolidity/lsp/SemanticTokensBuilder.cpp
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
#include <libsolidity/lsp/SemanticTokensBuilder.h>
|
||||||
|
#include <libsolidity/lsp/Utils.h>
|
||||||
|
|
||||||
|
#include <liblangutil/CharStream.h>
|
||||||
|
#include <liblangutil/SourceLocation.h>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity::langutil;
|
||||||
|
using namespace solidity::frontend;
|
||||||
|
|
||||||
|
namespace solidity::lsp
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
optional<SemanticTokenType> semanticTokenTypeForType(frontend::Type const* _type)
|
||||||
|
{
|
||||||
|
if (!_type)
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
switch (_type->category())
|
||||||
|
{
|
||||||
|
case frontend::Type::Category::Address: return SemanticTokenType::Class;
|
||||||
|
case frontend::Type::Category::Bool: return SemanticTokenType::Number;
|
||||||
|
case frontend::Type::Category::Enum: return SemanticTokenType::Enum;
|
||||||
|
case frontend::Type::Category::Function: return SemanticTokenType::Function;
|
||||||
|
case frontend::Type::Category::Integer: return SemanticTokenType::Number;
|
||||||
|
case frontend::Type::Category::RationalNumber: return SemanticTokenType::Number;
|
||||||
|
case frontend::Type::Category::StringLiteral: return SemanticTokenType::String;
|
||||||
|
case frontend::Type::Category::Struct: return SemanticTokenType::Struct;
|
||||||
|
case frontend::Type::Category::Contract: return SemanticTokenType::Class;
|
||||||
|
default:
|
||||||
|
lspDebug(fmt::format("semanticTokenTypeForType: unknown category: {}", static_cast<unsigned>(_type->category())));
|
||||||
|
return SemanticTokenType::Type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SemanticTokenType semanticTokenTypeForExpression(frontend::Type const* _type)
|
||||||
|
{
|
||||||
|
if (!_type)
|
||||||
|
return SemanticTokenType::Variable;
|
||||||
|
|
||||||
|
switch (_type->category())
|
||||||
|
{
|
||||||
|
case frontend::Type::Category::Enum:
|
||||||
|
return SemanticTokenType::Enum;
|
||||||
|
default:
|
||||||
|
return SemanticTokenType::Variable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace
|
||||||
|
|
||||||
|
Json::Value SemanticTokensBuilder::build(SourceUnit const& _sourceUnit, CharStream const& _charStream)
|
||||||
|
{
|
||||||
|
reset(&_charStream);
|
||||||
|
_sourceUnit.accept(*this);
|
||||||
|
return m_encodedTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SemanticTokensBuilder::reset(CharStream const* _charStream)
|
||||||
|
{
|
||||||
|
m_encodedTokens = Json::arrayValue;
|
||||||
|
m_charStream = _charStream;
|
||||||
|
m_lastLine = 0;
|
||||||
|
m_lastStartChar = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SemanticTokensBuilder::encode(
|
||||||
|
SourceLocation const& _sourceLocation,
|
||||||
|
SemanticTokenType _tokenType,
|
||||||
|
SemanticTokenModifiers _modifiers
|
||||||
|
)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocument_semanticTokens
|
||||||
|
|
||||||
|
// Step-1: Absolute positions
|
||||||
|
{ line: 2, startChar: 5, length: 3, tokenType: 0, tokenModifiers: 3 },
|
||||||
|
{ line: 2, startChar: 10, length: 4, tokenType: 1, tokenModifiers: 0 },
|
||||||
|
{ line: 5, startChar: 2, length: 7, tokenType: 2, tokenModifiers: 0 }
|
||||||
|
|
||||||
|
// Step-2: Relative positions as intermediate step
|
||||||
|
{ deltaLine: 2, deltaStartChar: 5, length: 3, tokenType: 0, tokenModifiers: 3 },
|
||||||
|
{ deltaLine: 0, deltaStartChar: 5, length: 4, tokenType: 1, tokenModifiers: 0 },
|
||||||
|
{ deltaLine: 3, deltaStartChar: 2, length: 7, tokenType: 2, tokenModifiers: 0 }
|
||||||
|
|
||||||
|
// Step-3: final array result
|
||||||
|
// 1st token, 2nd token, 3rd token
|
||||||
|
[ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ]
|
||||||
|
|
||||||
|
So traverse through the AST and assign each leaf a token 5-tuple.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// solAssert(_sourceLocation.isValid());
|
||||||
|
if (!_sourceLocation.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto const [line, startChar] = m_charStream->translatePositionToLineColumn(_sourceLocation.start);
|
||||||
|
auto const length = _sourceLocation.end - _sourceLocation.start;
|
||||||
|
|
||||||
|
lspDebug(fmt::format("encode [{}:{}..{}] {}", line, startChar, length, _tokenType));
|
||||||
|
|
||||||
|
m_encodedTokens.append(line - m_lastLine);
|
||||||
|
if (line == m_lastLine)
|
||||||
|
m_encodedTokens.append(startChar - m_lastStartChar);
|
||||||
|
else
|
||||||
|
m_encodedTokens.append(startChar);
|
||||||
|
m_encodedTokens.append(length);
|
||||||
|
m_encodedTokens.append(static_cast<int>(_tokenType));
|
||||||
|
m_encodedTokens.append(static_cast<int>(_modifiers));
|
||||||
|
|
||||||
|
m_lastLine = line;
|
||||||
|
m_lastStartChar = startChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SemanticTokensBuilder::visit(frontend::ContractDefinition const& _node)
|
||||||
|
{
|
||||||
|
encode(_node.nameLocation(), SemanticTokenType::Class);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SemanticTokensBuilder::visit(frontend::ElementaryTypeName const& _node)
|
||||||
|
{
|
||||||
|
encode(_node.location(), SemanticTokenType::Type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SemanticTokensBuilder::visit(frontend::ElementaryTypeNameExpression const& _node)
|
||||||
|
{
|
||||||
|
if (auto const tokenType = semanticTokenTypeForType(_node.annotation().type); tokenType.has_value())
|
||||||
|
encode(_node.location(), tokenType.value());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SemanticTokensBuilder::visit(frontend::EnumDefinition const& _node)
|
||||||
|
{
|
||||||
|
encode(_node.nameLocation(), SemanticTokenType::Enum);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SemanticTokensBuilder::visit(frontend::EnumValue const& _node)
|
||||||
|
{
|
||||||
|
encode(_node.nameLocation(), SemanticTokenType::EnumMember);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SemanticTokensBuilder::visit(frontend::ErrorDefinition const& _node)
|
||||||
|
{
|
||||||
|
encode(_node.nameLocation(), SemanticTokenType::Event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SemanticTokensBuilder::visit(frontend::FunctionDefinition const& _node)
|
||||||
|
{
|
||||||
|
encode(_node.nameLocation(), SemanticTokenType::Function);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SemanticTokensBuilder::visit(frontend::ModifierDefinition const& _node)
|
||||||
|
{
|
||||||
|
encode(_node.nameLocation(), SemanticTokenType::Modifier);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SemanticTokensBuilder::endVisit(frontend::Literal const& _literal)
|
||||||
|
{
|
||||||
|
encode(_literal.location(), SemanticTokenType::Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SemanticTokensBuilder::endVisit(frontend::StructuredDocumentation const& _documentation)
|
||||||
|
{
|
||||||
|
encode(_documentation.location(), SemanticTokenType::Comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SemanticTokensBuilder::endVisit(frontend::Identifier const& _identifier)
|
||||||
|
{
|
||||||
|
lspDebug(fmt::format("Identifier: {}, {}..{} cat={}", _identifier.name(), _identifier.location().start, _identifier.location().end, _identifier.annotation().type->category()));
|
||||||
|
|
||||||
|
SemanticTokenModifiers modifiers = SemanticTokenModifiers::None;
|
||||||
|
if (_identifier.annotation().isConstant.set() && *_identifier.annotation().isConstant)
|
||||||
|
{
|
||||||
|
lspDebug("OMG We've found a const!");
|
||||||
|
modifiers = modifiers | SemanticTokenModifiers::Readonly;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode(_identifier.location(), semanticTokenTypeForExpression(_identifier.annotation().type), modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SemanticTokensBuilder::endVisit(frontend::IdentifierPath const& _node)
|
||||||
|
{
|
||||||
|
lspDebug(fmt::format("IdentifierPath: identifier path [{}..{}]", _node.location().start, _node.location().end));
|
||||||
|
for (size_t i = 0; i < _node.path().size(); ++i)
|
||||||
|
lspDebug(fmt::format(" [{}]: {}", i, _node.path().at(i)));
|
||||||
|
if (dynamic_cast<EnumDefinition const*>(_node.annotation().referencedDeclaration))
|
||||||
|
encode(_node.location(), SemanticTokenType::EnumMember);
|
||||||
|
else
|
||||||
|
encode(_node.location(), SemanticTokenType::Variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SemanticTokensBuilder::visit(frontend::MemberAccess const& _node)
|
||||||
|
{
|
||||||
|
lspDebug(fmt::format("[{}..{}] MemberAccess({}): {}", _node.location().start, _node.location().end, _node.annotation().referencedDeclaration ? _node.annotation().referencedDeclaration->name() : "?", _node.memberName()));
|
||||||
|
|
||||||
|
auto const memberNameLength = static_cast<int>(_node.memberName().size());
|
||||||
|
auto const memberTokenType = semanticTokenTypeForExpression(_node.annotation().type);
|
||||||
|
|
||||||
|
auto lhsLocation = _node.location();
|
||||||
|
lhsLocation.end -= (memberNameLength + 1 /*exclude the dot*/);
|
||||||
|
|
||||||
|
auto rhsLocation = _node.location();
|
||||||
|
rhsLocation.start = rhsLocation.end - static_cast<int>(memberNameLength);
|
||||||
|
|
||||||
|
if (memberTokenType == SemanticTokenType::Enum)
|
||||||
|
{
|
||||||
|
// Special handling for enumeration symbols.
|
||||||
|
encode(lhsLocation, SemanticTokenType::Enum);
|
||||||
|
encode(rhsLocation, SemanticTokenType::EnumMember);
|
||||||
|
}
|
||||||
|
else if (memberTokenType == SemanticTokenType::Function)
|
||||||
|
{
|
||||||
|
// Special handling for function symbols.
|
||||||
|
encode(lhsLocation, SemanticTokenType::Variable);
|
||||||
|
encode(rhsLocation, memberTokenType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
encode(rhsLocation, memberTokenType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // we handle LHS and RHS explicitly above.
|
||||||
|
}
|
||||||
|
|
||||||
|
void SemanticTokensBuilder::endVisit(PragmaDirective const& _pragma)
|
||||||
|
{
|
||||||
|
encode(_pragma.location(), SemanticTokenType::Macro);
|
||||||
|
// NOTE: It would be nice if we could highlight based on the symbols,
|
||||||
|
// such as (version) numerics be different than identifiers.
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SemanticTokensBuilder::visit(frontend::UserDefinedTypeName const& _node)
|
||||||
|
{
|
||||||
|
if (auto const token = semanticTokenTypeForType(_node.annotation().type); token.has_value())
|
||||||
|
encode(_node.location(), *token);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SemanticTokensBuilder::visit(frontend::VariableDeclaration const& _node)
|
||||||
|
{
|
||||||
|
lspDebug(fmt::format("VariableDeclaration: {}", _node.name()));
|
||||||
|
|
||||||
|
if (auto const token = semanticTokenTypeForType(_node.typeName().annotation().type); token.has_value())
|
||||||
|
encode(_node.typeName().location(), *token);
|
||||||
|
|
||||||
|
encode(_node.nameLocation(), SemanticTokenType::Variable);
|
||||||
|
if (_node.overrides())
|
||||||
|
_node.overrides()->accept(*this);
|
||||||
|
if (_node.value())
|
||||||
|
_node.value()->accept(*this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace
|
123
libsolidity/lsp/SemanticTokensBuilder.h
Normal file
123
libsolidity/lsp/SemanticTokensBuilder.h
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
namespace solidity::langutil
|
||||||
|
{
|
||||||
|
class CharStream;
|
||||||
|
struct SourceLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace solidity::lsp
|
||||||
|
{
|
||||||
|
|
||||||
|
// See: https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#semanticTokenTypes
|
||||||
|
enum class SemanticTokenType
|
||||||
|
{
|
||||||
|
Class,
|
||||||
|
Comment,
|
||||||
|
Enum,
|
||||||
|
EnumMember,
|
||||||
|
Event,
|
||||||
|
Function,
|
||||||
|
Interface,
|
||||||
|
Keyword,
|
||||||
|
Macro,
|
||||||
|
Method,
|
||||||
|
Modifier,
|
||||||
|
Number,
|
||||||
|
Operator,
|
||||||
|
Parameter,
|
||||||
|
Property,
|
||||||
|
String,
|
||||||
|
Struct,
|
||||||
|
Type,
|
||||||
|
TypeParameter,
|
||||||
|
Variable,
|
||||||
|
|
||||||
|
// Unused below:
|
||||||
|
// Namespace,
|
||||||
|
// Regexp,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SemanticTokenModifiers
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
// Member integer values must be bit-values as
|
||||||
|
// they can be OR'd together.
|
||||||
|
Abstract = 0x0001,
|
||||||
|
Declaration = 0x0002,
|
||||||
|
Definition = 0x0004,
|
||||||
|
Deprecated = 0x0008,
|
||||||
|
Documentation = 0x0010,
|
||||||
|
Modification = 0x0020,
|
||||||
|
Readonly = 0x0040,
|
||||||
|
|
||||||
|
// Unused below:
|
||||||
|
// Static,
|
||||||
|
// Async,
|
||||||
|
// DefaultLibrary,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr SemanticTokenModifiers operator|(SemanticTokenModifiers a, SemanticTokenModifiers b) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<SemanticTokenModifiers>(static_cast<int>(a) | static_cast<int>(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
class SemanticTokensBuilder: public frontend::ASTConstVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Json::Value build(frontend::SourceUnit const& _sourceUnit, langutil::CharStream const& _charStream);
|
||||||
|
|
||||||
|
void reset(langutil::CharStream const* _charStream);
|
||||||
|
void encode(
|
||||||
|
langutil::SourceLocation const& _sourceLocation,
|
||||||
|
SemanticTokenType _tokenType,
|
||||||
|
SemanticTokenModifiers _modifiers = SemanticTokenModifiers::None
|
||||||
|
);
|
||||||
|
|
||||||
|
bool visit(frontend::ContractDefinition const&) override;
|
||||||
|
bool visit(frontend::ElementaryTypeName const&) override;
|
||||||
|
bool visit(frontend::ElementaryTypeNameExpression const&) override;
|
||||||
|
bool visit(frontend::EnumDefinition const&) override;
|
||||||
|
bool visit(frontend::EnumValue const&) override;
|
||||||
|
bool visit(frontend::ErrorDefinition const&) override;
|
||||||
|
bool visit(frontend::FunctionDefinition const&) override;
|
||||||
|
bool visit(frontend::ModifierDefinition const&) override;
|
||||||
|
void endVisit(frontend::Literal const&) override;
|
||||||
|
void endVisit(frontend::StructuredDocumentation const&) override;
|
||||||
|
void endVisit(frontend::Identifier const&) override;
|
||||||
|
void endVisit(frontend::IdentifierPath const&) override;
|
||||||
|
bool visit(frontend::MemberAccess const&) override;
|
||||||
|
void endVisit(frontend::PragmaDirective const&) override;
|
||||||
|
bool visit(frontend::UserDefinedTypeName const&) override;
|
||||||
|
bool visit(frontend::VariableDeclaration const&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Json::Value m_encodedTokens;
|
||||||
|
langutil::CharStream const* m_charStream;
|
||||||
|
int m_lastLine;
|
||||||
|
int m_lastStartChar;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end namespace
|
42
test/libsolidity/lsp/semanticTokens/enums.sol
Normal file
42
test/libsolidity/lsp/semanticTokens/enums.sol
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity >=0.8.0;
|
||||||
|
|
||||||
|
enum Weather {
|
||||||
|
Sunny,
|
||||||
|
Cloudy,
|
||||||
|
Rainy
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Color {
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Blue
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColorEnum() pure returns (Color result)
|
||||||
|
{
|
||||||
|
result = Color.Red;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
// -> textDocument/semanticTokens/full {
|
||||||
|
// }
|
||||||
|
// <- {
|
||||||
|
// "data": [
|
||||||
|
// 1, 0, 24, 8, 0,
|
||||||
|
// 2, 5, 7, 2, 0,
|
||||||
|
// 1, 4, 5, 3, 0,
|
||||||
|
// 1, 4, 6, 3, 0,
|
||||||
|
// 1, 4, 5, 3, 0,
|
||||||
|
// 3, 5, 5, 2, 0,
|
||||||
|
// 1, 4, 3, 3, 0,
|
||||||
|
// 1, 4, 5, 3, 0,
|
||||||
|
// 1, 4, 4, 3, 0,
|
||||||
|
// 3, 9, 12, 5, 0,
|
||||||
|
// 0, 29, 5, 2, 0,
|
||||||
|
// 0, 6, 6, 19, 0,
|
||||||
|
// 2, 4, 6, 2, 0,
|
||||||
|
// 0, 9, 5, 2, 0,
|
||||||
|
// 0, 6, 3, 3, 0
|
||||||
|
// ]
|
||||||
|
// }
|
52
test/libsolidity/lsp/semanticTokens/functions.sol
Normal file
52
test/libsolidity/lsp/semanticTokens/functions.sol
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity >=0.8.0;
|
||||||
|
|
||||||
|
library Lib
|
||||||
|
{
|
||||||
|
function add(uint a, uint b) public pure returns (uint result)
|
||||||
|
{
|
||||||
|
result = a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function warningWithUnused() public pure
|
||||||
|
{
|
||||||
|
uint unused;
|
||||||
|
// ^^^^^^^^^^^ @unusedVariable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Contract
|
||||||
|
{
|
||||||
|
function doNothing() pure public returns (bool)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
// functions: @unusedVariable 2072
|
||||||
|
// -> textDocument/semanticTokens/full {
|
||||||
|
// }
|
||||||
|
// <- {
|
||||||
|
// "data": [
|
||||||
|
// 1, 0, 24, 8, 0,
|
||||||
|
// 2, 8, 3, 0, 0,
|
||||||
|
// 2, 13, 3, 5, 0,
|
||||||
|
// 0, 4, 4, 11, 0,
|
||||||
|
// 0, 5, 1, 19, 0,
|
||||||
|
// 0, 3, 4, 11, 0,
|
||||||
|
// 0, 5, 1, 19, 0,
|
||||||
|
// 0, 24, 4, 11, 0,
|
||||||
|
// 0, 5, 6, 19, 0,
|
||||||
|
// 2, 8, 6, 19, 0,
|
||||||
|
// 0, 9, 1, 19, 0,
|
||||||
|
// 0, 4, 1, 19, 0,
|
||||||
|
// 3, 13, 17, 5, 0,
|
||||||
|
// 2, 8, 4, 11, 0,
|
||||||
|
// 0, 5, 6, 19, 0,
|
||||||
|
// 5, 9, 8, 0, 0,
|
||||||
|
// 2, 13, 9, 5, 0,
|
||||||
|
// 0, 33, 4, 11, 0,
|
||||||
|
// 2, 15, 4, 11, 0
|
||||||
|
// ]
|
||||||
|
// }
|
81
test/libsolidity/lsp/semanticTokens/modifiers.sol
Normal file
81
test/libsolidity/lsp/semanticTokens/modifiers.sol
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity >=0.8.0;
|
||||||
|
|
||||||
|
contract C
|
||||||
|
{
|
||||||
|
bool public locked = false;
|
||||||
|
int public calls = 0;
|
||||||
|
int public totalSum = 0;
|
||||||
|
|
||||||
|
function add(uint a, uint b) lock() monitor(a, b) public returns (uint result)
|
||||||
|
{
|
||||||
|
result = a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier lock()
|
||||||
|
{
|
||||||
|
require(!locked);
|
||||||
|
locked = true;
|
||||||
|
_;
|
||||||
|
locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier monitor(uint a, uint b)
|
||||||
|
{
|
||||||
|
calls++;
|
||||||
|
totalSum = totalSum + a + b;
|
||||||
|
// ^^^^^^^^^^^^^^^^ @totalSumWarning
|
||||||
|
// ^^^^^^^^^^^^ @totalSumWarningSub
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// modifiers: @totalSumWarningSub 2271 @totalSumWarning 2271
|
||||||
|
// -> textDocument/semanticTokens/full {
|
||||||
|
// }
|
||||||
|
// <- {
|
||||||
|
// "data": [
|
||||||
|
// 1, 0, 24, 8, 0,
|
||||||
|
// 2, 9, 1, 0, 0,
|
||||||
|
// 2, 4, 4, 11, 0,
|
||||||
|
// 0, 12, 6, 19, 0,
|
||||||
|
// 0, 9, 5, 11, 0,
|
||||||
|
// 1, 4, 3, 11, 0,
|
||||||
|
// 0, 11, 5, 19, 0,
|
||||||
|
// 0, 8, 1, 11, 0,
|
||||||
|
// 1, 4, 3, 11, 0,
|
||||||
|
// 0, 11, 8, 19, 0,
|
||||||
|
// 0, 11, 1, 11, 0,
|
||||||
|
// 2, 13, 3, 5, 0,
|
||||||
|
// 0, 4, 4, 11, 0,
|
||||||
|
// 0, 5, 1, 19, 0,
|
||||||
|
// 0, 3, 4, 11, 0,
|
||||||
|
// 0, 5, 1, 19, 0,
|
||||||
|
// 0, 40, 4, 11, 0,
|
||||||
|
// 0, 5, 6, 19, 0,
|
||||||
|
// 0, -42, 4, 19, 0,
|
||||||
|
// 0, 7, 7, 19, 0,
|
||||||
|
// 0, 8, 1, 19, 0,
|
||||||
|
// 0, 3, 1, 19, 0,
|
||||||
|
// 2, 8, 6, 19, 0,
|
||||||
|
// 0, 9, 1, 19, 0,
|
||||||
|
// 0, 4, 1, 19, 0,
|
||||||
|
// 3, 13, 4, 10, 0,
|
||||||
|
// 2, 8, 7, 19, 0,
|
||||||
|
// 0, 9, 6, 19, 0,
|
||||||
|
// 1, 8, 6, 19, 0,
|
||||||
|
// 0, 9, 4, 11, 0,
|
||||||
|
// 2, 8, 6, 19, 0,
|
||||||
|
// 0, 9, 5, 11, 0,
|
||||||
|
// 3, 13, 7, 10, 0,
|
||||||
|
// 0, 8, 4, 11, 0,
|
||||||
|
// 0, 5, 1, 19, 0,
|
||||||
|
// 0, 3, 4, 11, 0,
|
||||||
|
// 0, 5, 1, 19, 0,
|
||||||
|
// 2, 8, 5, 19, 0,
|
||||||
|
// 1, 8, 8, 19, 0,
|
||||||
|
// 0, 11, 8, 19, 0,
|
||||||
|
// 0, 11, 1, 19, 0,
|
||||||
|
// 0, 4, 1, 19, 0
|
||||||
|
// ]
|
||||||
|
// }
|
48
test/libsolidity/lsp/semanticTokens/structs.sol
Normal file
48
test/libsolidity/lsp/semanticTokens/structs.sol
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity >=0.8.0;
|
||||||
|
|
||||||
|
struct Tag
|
||||||
|
{
|
||||||
|
uint id;
|
||||||
|
string name;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RGBColor
|
||||||
|
{
|
||||||
|
uint8 red;
|
||||||
|
uint8 green;
|
||||||
|
uint8 blue;
|
||||||
|
Tag tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
function memberAccess(RGBColor memory color) pure returns(uint)
|
||||||
|
{
|
||||||
|
return color.red + color.green + color.blue;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// -> textDocument/semanticTokens/full {
|
||||||
|
// }
|
||||||
|
// <- {
|
||||||
|
// "data": [
|
||||||
|
// 1, 0, 24, 8, 0,
|
||||||
|
// 4, 4, 4, 11, 0,
|
||||||
|
// 0, 5, 2, 19, 0,
|
||||||
|
// 1, 4, 6, 17, 0,
|
||||||
|
// 0, 7, 4, 19, 0,
|
||||||
|
// 5, 4, 5, 11, 0,
|
||||||
|
// 0, 6, 3, 19, 0,
|
||||||
|
// 1, 4, 5, 11, 0,
|
||||||
|
// 0, 6, 5, 19, 0,
|
||||||
|
// 1, 4, 5, 11, 0,
|
||||||
|
// 0, 6, 4, 19, 0,
|
||||||
|
// 1, 4, 3, 16, 0,
|
||||||
|
// 0, 4, 3, 19, 0,
|
||||||
|
// 3, 9, 12, 5, 0,
|
||||||
|
// 0, 13, 8, 16, 0,
|
||||||
|
// 0, 16, 5, 19, 0,
|
||||||
|
// 0, 20, 4, 11, 0,
|
||||||
|
// 2, 17, 3, 19, 0,
|
||||||
|
// 0, 12, 5, 19, 0,
|
||||||
|
// 0, 14, 4, 19, 0
|
||||||
|
// ]
|
||||||
|
// }
|
Loading…
Reference in New Issue
Block a user