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/FileRepository.cpp
|
||||
lsp/FileRepository.h
|
||||
lsp/HandlerBase.cpp
|
||||
lsp/HandlerBase.h
|
||||
lsp/LanguageServer.cpp
|
||||
lsp/LanguageServer.h
|
||||
lsp/Transport.cpp
|
||||
lsp/Transport.h
|
||||
lsp/Utils.cpp
|
||||
lsp/Utils.h
|
||||
parsing/DocStringParser.cpp
|
||||
parsing/DocStringParser.h
|
||||
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/StandardCompiler.h>
|
||||
#include <libsolidity/lsp/LanguageServer.h>
|
||||
#include <libsolidity/lsp/HandlerBase.h>
|
||||
#include <libsolidity/lsp/Utils.h>
|
||||
|
||||
|
||||
#include <liblangutil/SourceReferenceExtractor.h>
|
||||
#include <liblangutil/CharStream.h>
|
||||
@ -48,31 +51,6 @@ using namespace solidity::frontend;
|
||||
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)
|
||||
{
|
||||
// 1=Error, 2=Warning, 3=Info, 4=Hint
|
||||
@ -107,55 +85,19 @@ LanguageServer::LanguageServer(Transport& _transport):
|
||||
{
|
||||
}
|
||||
|
||||
optional<SourceLocation> LanguageServer::parsePosition(
|
||||
string const& _sourceUnitName,
|
||||
Json::Value const& _position
|
||||
) const
|
||||
optional<SourceLocation> LanguageServer::parseRange(string const& _sourceUnitName, Json::Value const& _range)
|
||||
{
|
||||
if (!m_fileRepository.sourceUnits().count(_sourceUnitName))
|
||||
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;
|
||||
return HandlerBase{*this}.parseRange(_sourceUnitName, _range);
|
||||
}
|
||||
|
||||
optional<SourceLocation> LanguageServer::parseRange(string const& _sourceUnitName, Json::Value const& _range) const
|
||||
Json::Value LanguageServer::toRange(SourceLocation const& _location)
|
||||
{
|
||||
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;
|
||||
return HandlerBase(*this).toRange(_location);
|
||||
}
|
||||
|
||||
Json::Value LanguageServer::toRange(SourceLocation const& _location) const
|
||||
Json::Value LanguageServer::toJson(SourceLocation const& _location)
|
||||
{
|
||||
if (!_location.hasText())
|
||||
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;
|
||||
return HandlerBase(*this).toJson(_location);
|
||||
}
|
||||
|
||||
void LanguageServer::changeConfiguration(Json::Value const& _settings)
|
||||
@ -403,3 +345,20 @@ void LanguageServer::handleTextDocumentDidClose(Json::Value const& _args)
|
||||
|
||||
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.
|
||||
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:
|
||||
/// 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.
|
||||
@ -72,22 +77,18 @@ private:
|
||||
|
||||
/// Compile everything until after analysis phase.
|
||||
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,
|
||||
/// or nullopt on failure.
|
||||
std::optional<langutil::SourceLocation> parseRange(
|
||||
std::string const& _sourceUnitName,
|
||||
Json::Value const& _range
|
||||
) const;
|
||||
Json::Value toRange(langutil::SourceLocation const& _location) const;
|
||||
Json::Value toJson(langutil::SourceLocation const& _location) const;
|
||||
);
|
||||
Json::Value toRange(langutil::SourceLocation const& _location);
|
||||
Json::Value toJson(langutil::SourceLocation const& _location);
|
||||
|
||||
// LSP related member fields
|
||||
using MessageHandler = std::function<void(MessageID, Json::Value const&)>;
|
||||
|
||||
enum class State { Started, Initialized, ShutdownRequested, ExitRequested, ExitWithoutShutdown };
|
||||
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