mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
LSP: Introduce HandlerBase for future LSP-feature implementations.
This commit is contained in:
parent
60463cfd11
commit
1035eacb53
@ -159,8 +159,14 @@ set(sources
|
|||||||
lsp/LanguageServer.h
|
lsp/LanguageServer.h
|
||||||
lsp/FileRepository.cpp
|
lsp/FileRepository.cpp
|
||||||
lsp/FileRepository.h
|
lsp/FileRepository.h
|
||||||
|
lsp/HandlerBase.cpp
|
||||||
|
lsp/HandlerBase.h
|
||||||
|
lsp/LanguageServer.cpp
|
||||||
|
lsp/LanguageServer.h
|
||||||
lsp/Transport.cpp
|
lsp/Transport.cpp
|
||||||
lsp/Transport.h
|
lsp/Transport.h
|
||||||
|
lsp/Utils.cpp
|
||||||
|
lsp/Utils.h
|
||||||
parsing/DocStringParser.cpp
|
parsing/DocStringParser.cpp
|
||||||
parsing/DocStringParser.h
|
parsing/DocStringParser.h
|
||||||
parsing/Parser.cpp
|
parsing/Parser.cpp
|
||||||
|
63
libsolidity/lsp/HandlerBase.cpp
Normal file
63
libsolidity/lsp/HandlerBase.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include <libsolidity/lsp/HandlerBase.h>
|
||||||
|
#include <libsolidity/lsp/LanguageServer.h>
|
||||||
|
#include <libsolidity/lsp/Utils.h>
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
|
||||||
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace solidity::lsp
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace langutil;
|
||||||
|
|
||||||
|
Json::Value HandlerBase::toRange(SourceLocation const& _location) const
|
||||||
|
{
|
||||||
|
if (!_location.hasText())
|
||||||
|
return toJsonRange({}, {});
|
||||||
|
|
||||||
|
solAssert(_location.sourceName, "");
|
||||||
|
langutil::CharStream const& stream = charStreamProvider().charStream(*_location.sourceName);
|
||||||
|
LineColumn start = stream.translatePositionToLineColumn(_location.start);
|
||||||
|
LineColumn end = stream.translatePositionToLineColumn(_location.end);
|
||||||
|
return toJsonRange(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value HandlerBase::toJson(SourceLocation const& _location) const
|
||||||
|
{
|
||||||
|
solAssert(_location.sourceName);
|
||||||
|
Json::Value item = Json::objectValue;
|
||||||
|
item["uri"] = fileRepository().sourceUnitNameToClientPath(*_location.sourceName);
|
||||||
|
item["range"] = toRange(_location);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<SourceLocation> HandlerBase::parsePosition(string const& _sourceUnitName, Json::Value const& _position) const
|
||||||
|
{
|
||||||
|
if (!fileRepository().sourceUnits().count(_sourceUnitName))
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
if (optional<LineColumn> lineColumn = parseLineColumn(_position))
|
||||||
|
if (optional<int> const offset = CharStream::translateLineColumnToPosition(
|
||||||
|
fileRepository().sourceUnits().at(_sourceUnitName),
|
||||||
|
*lineColumn
|
||||||
|
))
|
||||||
|
return SourceLocation{*offset, *offset, make_shared<string>(_sourceUnitName)};
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<SourceLocation> HandlerBase::parseRange(string const& _sourceUnitName, Json::Value const& _range) const
|
||||||
|
{
|
||||||
|
if (!_range.isObject())
|
||||||
|
return nullopt;
|
||||||
|
optional<SourceLocation> start = parsePosition(_sourceUnitName, _range["start"]);
|
||||||
|
optional<SourceLocation> end = parsePosition(_sourceUnitName, _range["end"]);
|
||||||
|
if (!start || !end)
|
||||||
|
return nullopt;
|
||||||
|
solAssert(*start->sourceName == *end->sourceName);
|
||||||
|
start->end = end->end;
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
46
libsolidity/lsp/HandlerBase.h
Normal file
46
libsolidity/lsp/HandlerBase.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/lsp/FileRepository.h>
|
||||||
|
#include <libsolidity/lsp/LanguageServer.h>
|
||||||
|
|
||||||
|
#include <liblangutil/SourceLocation.h>
|
||||||
|
#include <liblangutil/CharStreamProvider.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace solidity::lsp
|
||||||
|
{
|
||||||
|
|
||||||
|
class Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper base class for implementing handlers.
|
||||||
|
*/
|
||||||
|
class HandlerBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit HandlerBase(LanguageServer& _server): m_server{_server} {}
|
||||||
|
|
||||||
|
Json::Value toRange(langutil::SourceLocation const& _location) const;
|
||||||
|
Json::Value toJson(langutil::SourceLocation const& _location) const;
|
||||||
|
|
||||||
|
std::optional<langutil::SourceLocation> parsePosition(
|
||||||
|
std::string const& _sourceUnitName,
|
||||||
|
Json::Value const& _position
|
||||||
|
) const;
|
||||||
|
|
||||||
|
/// @returns the source location given a source unit name and an LSP Range object,
|
||||||
|
/// or nullopt on failure.
|
||||||
|
std::optional<langutil::SourceLocation> parseRange(
|
||||||
|
std::string const& _sourceUnitName,
|
||||||
|
Json::Value const& _range
|
||||||
|
) const;
|
||||||
|
|
||||||
|
langutil::CharStreamProvider const& charStreamProvider() const noexcept { return m_server.charStreamProvider(); };
|
||||||
|
FileRepository const& fileRepository() const noexcept { return m_server.fileRepository(); };
|
||||||
|
Transport& client() const noexcept { return m_server.client(); };
|
||||||
|
|
||||||
|
LanguageServer& m_server;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -21,6 +21,9 @@
|
|||||||
#include <libsolidity/interface/ReadFile.h>
|
#include <libsolidity/interface/ReadFile.h>
|
||||||
#include <libsolidity/interface/StandardCompiler.h>
|
#include <libsolidity/interface/StandardCompiler.h>
|
||||||
#include <libsolidity/lsp/LanguageServer.h>
|
#include <libsolidity/lsp/LanguageServer.h>
|
||||||
|
#include <libsolidity/lsp/HandlerBase.h>
|
||||||
|
#include <libsolidity/lsp/Utils.h>
|
||||||
|
|
||||||
|
|
||||||
#include <liblangutil/SourceReferenceExtractor.h>
|
#include <liblangutil/SourceReferenceExtractor.h>
|
||||||
#include <liblangutil/CharStream.h>
|
#include <liblangutil/CharStream.h>
|
||||||
@ -48,31 +51,6 @@ using namespace solidity::frontend;
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
Json::Value toJson(LineColumn _pos)
|
|
||||||
{
|
|
||||||
Json::Value json = Json::objectValue;
|
|
||||||
json["line"] = max(_pos.line, 0);
|
|
||||||
json["character"] = max(_pos.column, 0);
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value toJsonRange(LineColumn const& _start, LineColumn const& _end)
|
|
||||||
{
|
|
||||||
Json::Value json;
|
|
||||||
json["start"] = toJson(_start);
|
|
||||||
json["end"] = toJson(_end);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional<LineColumn> parseLineColumn(Json::Value const& _lineColumn)
|
|
||||||
{
|
|
||||||
if (_lineColumn.isObject() && _lineColumn["line"].isInt() && _lineColumn["character"].isInt())
|
|
||||||
return LineColumn{_lineColumn["line"].asInt(), _lineColumn["character"].asInt()};
|
|
||||||
else
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
int toDiagnosticSeverity(Error::Type _errorType)
|
int toDiagnosticSeverity(Error::Type _errorType)
|
||||||
{
|
{
|
||||||
// 1=Error, 2=Warning, 3=Info, 4=Hint
|
// 1=Error, 2=Warning, 3=Info, 4=Hint
|
||||||
@ -107,55 +85,19 @@ LanguageServer::LanguageServer(Transport& _transport):
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<SourceLocation> LanguageServer::parsePosition(
|
optional<SourceLocation> LanguageServer::parseRange(string const& _sourceUnitName, Json::Value const& _range)
|
||||||
string const& _sourceUnitName,
|
|
||||||
Json::Value const& _position
|
|
||||||
) const
|
|
||||||
{
|
{
|
||||||
if (!m_fileRepository.sourceUnits().count(_sourceUnitName))
|
return HandlerBase{*this}.parseRange(_sourceUnitName, _range);
|
||||||
return nullopt;
|
|
||||||
|
|
||||||
if (optional<LineColumn> lineColumn = parseLineColumn(_position))
|
|
||||||
if (optional<int> const offset = CharStream::translateLineColumnToPosition(
|
|
||||||
m_fileRepository.sourceUnits().at(_sourceUnitName),
|
|
||||||
*lineColumn
|
|
||||||
))
|
|
||||||
return SourceLocation{*offset, *offset, make_shared<string>(_sourceUnitName)};
|
|
||||||
return nullopt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<SourceLocation> LanguageServer::parseRange(string const& _sourceUnitName, Json::Value const& _range) const
|
Json::Value LanguageServer::toRange(SourceLocation const& _location)
|
||||||
{
|
{
|
||||||
if (!_range.isObject())
|
return HandlerBase(*this).toRange(_location);
|
||||||
return nullopt;
|
|
||||||
optional<SourceLocation> start = parsePosition(_sourceUnitName, _range["start"]);
|
|
||||||
optional<SourceLocation> end = parsePosition(_sourceUnitName, _range["end"]);
|
|
||||||
if (!start || !end)
|
|
||||||
return nullopt;
|
|
||||||
solAssert(*start->sourceName == *end->sourceName);
|
|
||||||
start->end = end->end;
|
|
||||||
return start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value LanguageServer::toRange(SourceLocation const& _location) const
|
Json::Value LanguageServer::toJson(SourceLocation const& _location)
|
||||||
{
|
{
|
||||||
if (!_location.hasText())
|
return HandlerBase(*this).toJson(_location);
|
||||||
return toJsonRange({}, {});
|
|
||||||
|
|
||||||
solAssert(_location.sourceName, "");
|
|
||||||
CharStream const& stream = m_compilerStack.charStream(*_location.sourceName);
|
|
||||||
LineColumn start = stream.translatePositionToLineColumn(_location.start);
|
|
||||||
LineColumn end = stream.translatePositionToLineColumn(_location.end);
|
|
||||||
return toJsonRange(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value LanguageServer::toJson(SourceLocation const& _location) const
|
|
||||||
{
|
|
||||||
solAssert(_location.sourceName);
|
|
||||||
Json::Value item = Json::objectValue;
|
|
||||||
item["uri"] = m_fileRepository.sourceUnitNameToClientPath(*_location.sourceName);
|
|
||||||
item["range"] = toRange(_location);
|
|
||||||
return item;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageServer::changeConfiguration(Json::Value const& _settings)
|
void LanguageServer::changeConfiguration(Json::Value const& _settings)
|
||||||
@ -403,3 +345,20 @@ void LanguageServer::handleTextDocumentDidClose(Json::Value const& _args)
|
|||||||
|
|
||||||
compileAndUpdateDiagnostics();
|
compileAndUpdateDiagnostics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASTNode const* LanguageServer::requestASTNode(std::string const& _sourceUnitName, LineColumn const& _filePos)
|
||||||
|
{
|
||||||
|
if (m_compilerStack.state() < CompilerStack::AnalysisPerformed)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!m_fileRepository.sourceUnits().count(_sourceUnitName))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
optional<int> sourcePos = m_compilerStack.charStream(_sourceUnitName)
|
||||||
|
.translateLineColumnToPosition(_filePos);
|
||||||
|
if (!sourcePos.has_value())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return locateInnermostASTNode(*sourcePos, m_compilerStack.ast(_sourceUnitName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,11 @@ public:
|
|||||||
/// @return boolean indicating normal or abnormal termination.
|
/// @return boolean indicating normal or abnormal termination.
|
||||||
bool run();
|
bool run();
|
||||||
|
|
||||||
|
FileRepository& fileRepository() noexcept { return m_fileRepository; }
|
||||||
|
Transport& client() noexcept { return m_client; }
|
||||||
|
frontend::ASTNode const* requestASTNode(std::string const& _sourceUnitName, langutil::LineColumn const& _filePos);
|
||||||
|
langutil::CharStreamProvider const& charStreamProvider() const noexcept { return m_compilerStack; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Checks if the server is initialized (to be used by messages that need it to be initialized).
|
/// Checks if the server is initialized (to be used by messages that need it to be initialized).
|
||||||
/// Reports an error and returns false if not.
|
/// Reports an error and returns false if not.
|
||||||
@ -72,22 +77,18 @@ private:
|
|||||||
|
|
||||||
/// Compile everything until after analysis phase.
|
/// Compile everything until after analysis phase.
|
||||||
void compile();
|
void compile();
|
||||||
|
using MessageHandler = std::function<void(MessageID, Json::Value const&)>;
|
||||||
|
|
||||||
std::optional<langutil::SourceLocation> parsePosition(
|
|
||||||
std::string const& _sourceUnitName,
|
|
||||||
Json::Value const& _position
|
|
||||||
) const;
|
|
||||||
/// @returns the source location given a source unit name and an LSP Range object,
|
/// @returns the source location given a source unit name and an LSP Range object,
|
||||||
/// or nullopt on failure.
|
/// or nullopt on failure.
|
||||||
std::optional<langutil::SourceLocation> parseRange(
|
std::optional<langutil::SourceLocation> parseRange(
|
||||||
std::string const& _sourceUnitName,
|
std::string const& _sourceUnitName,
|
||||||
Json::Value const& _range
|
Json::Value const& _range
|
||||||
) const;
|
);
|
||||||
Json::Value toRange(langutil::SourceLocation const& _location) const;
|
Json::Value toRange(langutil::SourceLocation const& _location);
|
||||||
Json::Value toJson(langutil::SourceLocation const& _location) const;
|
Json::Value toJson(langutil::SourceLocation const& _location);
|
||||||
|
|
||||||
// LSP related member fields
|
// LSP related member fields
|
||||||
using MessageHandler = std::function<void(MessageID, Json::Value const&)>;
|
|
||||||
|
|
||||||
enum class State { Started, Initialized, ShutdownRequested, ExitRequested, ExitWithoutShutdown };
|
enum class State { Started, Initialized, ShutdownRequested, ExitRequested, ExitWithoutShutdown };
|
||||||
State m_state = State::Started;
|
State m_state = State::Started;
|
||||||
|
70
libsolidity/lsp/Utils.cpp
Normal file
70
libsolidity/lsp/Utils.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#include <liblangutil/CharStreamProvider.h>
|
||||||
|
#include <liblangutil/Exceptions.h>
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
#include <libsolidity/lsp/FileRepository.h>
|
||||||
|
#include <libsolidity/lsp/Utils.h>
|
||||||
|
|
||||||
|
namespace solidity::lsp
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace frontend;
|
||||||
|
using namespace langutil;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
optional<LineColumn> parseLineColumn(Json::Value const& _lineColumn)
|
||||||
|
{
|
||||||
|
if (_lineColumn.isObject() && _lineColumn["line"].isInt() && _lineColumn["character"].isInt())
|
||||||
|
return LineColumn{_lineColumn["line"].asInt(), _lineColumn["character"].asInt()};
|
||||||
|
else
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value toJson(LineColumn _pos)
|
||||||
|
{
|
||||||
|
Json::Value json = Json::objectValue;
|
||||||
|
json["line"] = max(_pos.line, 0);
|
||||||
|
json["character"] = max(_pos.column, 0);
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value toJsonRange(LineColumn const& _start, LineColumn const& _end)
|
||||||
|
{
|
||||||
|
Json::Value json;
|
||||||
|
json["start"] = toJson(_start);
|
||||||
|
json["end"] = toJson(_end);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<Declaration const*> allAnnotatedDeclarations(Expression const* _expression)
|
||||||
|
{
|
||||||
|
vector<Declaration const*> output;
|
||||||
|
|
||||||
|
if (auto const* identifier = dynamic_cast<Identifier const*>(_expression))
|
||||||
|
{
|
||||||
|
output.push_back(identifier->annotation().referencedDeclaration);
|
||||||
|
output += identifier->annotation().candidateDeclarations;
|
||||||
|
}
|
||||||
|
else if (auto const* memberAccess = dynamic_cast<MemberAccess const*>(_expression))
|
||||||
|
{
|
||||||
|
output.push_back(memberAccess->annotation().referencedDeclaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<SourceLocation> declarationPosition(Declaration const* _declaration)
|
||||||
|
{
|
||||||
|
if (!_declaration)
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
if (_declaration->nameLocation().isValid())
|
||||||
|
return _declaration->nameLocation();
|
||||||
|
|
||||||
|
if (_declaration->location().isValid())
|
||||||
|
return _declaration->location();
|
||||||
|
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
libsolidity/lsp/Utils.h
Normal file
29
libsolidity/lsp/Utils.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <liblangutil/SourceLocation.h>
|
||||||
|
|
||||||
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
|
|
||||||
|
#include <libsolutil/JSON.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace solidity::langutil
|
||||||
|
{
|
||||||
|
class CharStreamProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace solidity::lsp
|
||||||
|
{
|
||||||
|
|
||||||
|
class FileRepository;
|
||||||
|
|
||||||
|
std::optional<langutil::LineColumn> parseLineColumn(Json::Value const& _lineColumn);
|
||||||
|
Json::Value toJson(langutil::LineColumn _pos);
|
||||||
|
Json::Value toJsonRange(langutil::LineColumn const& _start, langutil::LineColumn const& _end);
|
||||||
|
|
||||||
|
std::vector<frontend::Declaration const*> allAnnotatedDeclarations(frontend::Expression const* _expression);
|
||||||
|
std::optional<langutil::SourceLocation> declarationPosition(frontend::Declaration const* _declaration);
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user