mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
LSP: Introduce HandlerError(id, code, message) exception for easier handling.
This commit is contained in:
parent
75df03c8a8
commit
4105b0a587
@ -38,6 +38,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace std::string_literals;
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
|
||||||
using namespace solidity::lsp;
|
using namespace solidity::lsp;
|
||||||
@ -96,10 +97,10 @@ LanguageServer::LanguageServer(Transport& _transport):
|
|||||||
{"initialize", bind(&LanguageServer::handleInitialize, this, _1, _2)},
|
{"initialize", bind(&LanguageServer::handleInitialize, this, _1, _2)},
|
||||||
{"initialized", [](auto, auto) {}},
|
{"initialized", [](auto, auto) {}},
|
||||||
{"shutdown", [this](auto, auto) { m_state = State::ShutdownRequested; }},
|
{"shutdown", [this](auto, auto) { m_state = State::ShutdownRequested; }},
|
||||||
{"textDocument/didOpen", bind(&LanguageServer::handleTextDocumentDidOpen, this, _1, _2)},
|
{"textDocument/didOpen", bind(&LanguageServer::handleTextDocumentDidOpen, this, _2)},
|
||||||
{"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _1, _2)},
|
{"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _2)},
|
||||||
{"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _1, _2)},
|
{"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _2)},
|
||||||
{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _1, _2)},
|
{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _2)},
|
||||||
},
|
},
|
||||||
m_fileRepository("/" /* basePath */),
|
m_fileRepository("/" /* basePath */),
|
||||||
m_compilerStack{m_fileRepository.reader()}
|
m_compilerStack{m_fileRepository.reader()}
|
||||||
@ -260,6 +261,10 @@ bool LanguageServer::run()
|
|||||||
else
|
else
|
||||||
m_client.error({}, ErrorCode::ParseError, "\"method\" has to be a string.");
|
m_client.error({}, ErrorCode::ParseError, "\"method\" has to be a string.");
|
||||||
}
|
}
|
||||||
|
catch (RequestError const& error)
|
||||||
|
{
|
||||||
|
m_client.error(id, error.code(), error.comment() ? *error.comment() : ""s);
|
||||||
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
m_client.error(id, ErrorCode::InternalError, "Unhandled exception: "s + boost::current_exception_diagnostic_information());
|
m_client.error(id, ErrorCode::InternalError, "Unhandled exception: "s + boost::current_exception_diagnostic_information());
|
||||||
@ -268,24 +273,23 @@ bool LanguageServer::run()
|
|||||||
return m_state == State::ExitRequested;
|
return m_state == State::ExitRequested;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LanguageServer::checkServerInitialized(MessageID _id)
|
void LanguageServer::requireServerInitialized()
|
||||||
{
|
{
|
||||||
if (m_state != State::Initialized)
|
if (m_state != State::Initialized)
|
||||||
{
|
BOOST_THROW_EXCEPTION(
|
||||||
m_client.error(_id, ErrorCode::ServerNotInitialized, "Server is not properly initialized.");
|
RequestError(ErrorCode::ServerNotInitialized) <<
|
||||||
return false;
|
errinfo_comment("Server is not properly initialized.")
|
||||||
}
|
);
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
|
void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
|
||||||
{
|
{
|
||||||
if (m_state != State::Started)
|
if (m_state != State::Started)
|
||||||
{
|
BOOST_THROW_EXCEPTION(
|
||||||
m_client.error(_id, ErrorCode::RequestFailed, "Initialize called at the wrong time.");
|
RequestError(ErrorCode::RequestFailed) <<
|
||||||
return;
|
errinfo_comment("Initialize called at the wrong time.")
|
||||||
}
|
);
|
||||||
|
|
||||||
m_state = State::Initialized;
|
m_state = State::Initialized;
|
||||||
|
|
||||||
// The default of FileReader is to use `.`, but the path from where the LSP was started
|
// The default of FileReader is to use `.`, but the path from where the LSP was started
|
||||||
@ -295,10 +299,11 @@ void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
|
|||||||
{
|
{
|
||||||
rootPath = uri.asString();
|
rootPath = uri.asString();
|
||||||
if (!boost::starts_with(rootPath, "file://"))
|
if (!boost::starts_with(rootPath, "file://"))
|
||||||
{
|
BOOST_THROW_EXCEPTION(
|
||||||
m_client.error(_id, ErrorCode::InvalidParams, "rootUri only supports file URI scheme.");
|
RequestError(ErrorCode::InvalidParams) <<
|
||||||
return;
|
errinfo_comment("rootUri only supports file URI scheme.")
|
||||||
}
|
);
|
||||||
|
|
||||||
rootPath = rootPath.substr(7);
|
rootPath = rootPath.substr(7);
|
||||||
}
|
}
|
||||||
else if (Json::Value rootPath = _args["rootPath"])
|
else if (Json::Value rootPath = _args["rootPath"])
|
||||||
@ -317,23 +322,23 @@ void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
|
|||||||
m_client.reply(_id, move(replyArgs));
|
m_client.reply(_id, move(replyArgs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LanguageServer::handleWorkspaceDidChangeConfiguration(Json::Value const& _args)
|
||||||
void LanguageServer::handleWorkspaceDidChangeConfiguration(MessageID _id, Json::Value const& _args)
|
|
||||||
{
|
{
|
||||||
if (!checkServerInitialized(_id))
|
requireServerInitialized();
|
||||||
return;
|
|
||||||
|
|
||||||
if (_args["settings"].isObject())
|
if (_args["settings"].isObject())
|
||||||
changeConfiguration(_args["settings"]);
|
changeConfiguration(_args["settings"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageServer::handleTextDocumentDidOpen(MessageID _id, Json::Value const& _args)
|
void LanguageServer::handleTextDocumentDidOpen(Json::Value const& _args)
|
||||||
{
|
{
|
||||||
if (!checkServerInitialized(_id))
|
requireServerInitialized();
|
||||||
return;
|
|
||||||
|
|
||||||
if (!_args["textDocument"])
|
if (!_args["textDocument"])
|
||||||
m_client.error(_id, ErrorCode::RequestFailed, "Text document parameter missing.");
|
BOOST_THROW_EXCEPTION(
|
||||||
|
RequestError(ErrorCode::RequestFailed) <<
|
||||||
|
errinfo_comment("Text document parameter missing.")
|
||||||
|
);
|
||||||
|
|
||||||
string text = _args["textDocument"]["text"].asString();
|
string text = _args["textDocument"]["text"].asString();
|
||||||
string uri = _args["textDocument"]["uri"].asString();
|
string uri = _args["textDocument"]["uri"].asString();
|
||||||
@ -342,41 +347,37 @@ void LanguageServer::handleTextDocumentDidOpen(MessageID _id, Json::Value const&
|
|||||||
compileAndUpdateDiagnostics();
|
compileAndUpdateDiagnostics();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageServer::handleTextDocumentDidChange(MessageID _id, Json::Value const& _args)
|
void LanguageServer::handleTextDocumentDidChange(Json::Value const& _args)
|
||||||
{
|
{
|
||||||
if (!checkServerInitialized(_id))
|
requireServerInitialized();
|
||||||
return;
|
|
||||||
|
|
||||||
string const uri = _args["textDocument"]["uri"].asString();
|
string const uri = _args["textDocument"]["uri"].asString();
|
||||||
|
|
||||||
for (Json::Value jsonContentChange: _args["contentChanges"])
|
for (Json::Value jsonContentChange: _args["contentChanges"])
|
||||||
{
|
{
|
||||||
if (!jsonContentChange.isObject())
|
if (!jsonContentChange.isObject())
|
||||||
{
|
BOOST_THROW_EXCEPTION(
|
||||||
m_client.error(_id, ErrorCode::RequestFailed, "Invalid content reference.");
|
RequestError(ErrorCode::RequestFailed) <<
|
||||||
return;
|
errinfo_comment("Invalid content reference.")
|
||||||
}
|
);
|
||||||
|
|
||||||
string const sourceUnitName = m_fileRepository.clientPathToSourceUnitName(uri);
|
string const sourceUnitName = m_fileRepository.clientPathToSourceUnitName(uri);
|
||||||
if (!m_fileRepository.sourceUnits().count(sourceUnitName))
|
if (!m_fileRepository.sourceUnits().count(sourceUnitName))
|
||||||
{
|
BOOST_THROW_EXCEPTION(
|
||||||
m_client.error(_id, ErrorCode::RequestFailed, "Unknown file: " + uri);
|
RequestError(ErrorCode::RequestFailed) <<
|
||||||
return;
|
errinfo_comment("Unknown file: " + uri)
|
||||||
}
|
);
|
||||||
|
|
||||||
string text = jsonContentChange["text"].asString();
|
string text = jsonContentChange["text"].asString();
|
||||||
if (jsonContentChange["range"].isObject()) // otherwise full content update
|
if (jsonContentChange["range"].isObject()) // otherwise full content update
|
||||||
{
|
{
|
||||||
optional<SourceLocation> change = parseRange(sourceUnitName, jsonContentChange["range"]);
|
optional<SourceLocation> change = parseRange(sourceUnitName, jsonContentChange["range"]);
|
||||||
if (!change || !change->hasText())
|
if (!change || !change->hasText())
|
||||||
{
|
BOOST_THROW_EXCEPTION(
|
||||||
m_client.error(
|
RequestError(ErrorCode::RequestFailed) <<
|
||||||
_id,
|
errinfo_comment("Invalid source range: " + jsonCompactPrint(jsonContentChange["range"]))
|
||||||
ErrorCode::RequestFailed,
|
|
||||||
"Invalid source range: " + jsonCompactPrint(jsonContentChange["range"])
|
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}
|
|
||||||
string buffer = m_fileRepository.sourceUnits().at(sourceUnitName);
|
string buffer = m_fileRepository.sourceUnits().at(sourceUnitName);
|
||||||
buffer.replace(static_cast<size_t>(change->start), static_cast<size_t>(change->end - change->start), move(text));
|
buffer.replace(static_cast<size_t>(change->start), static_cast<size_t>(change->end - change->start), move(text));
|
||||||
text = move(buffer);
|
text = move(buffer);
|
||||||
@ -387,13 +388,15 @@ void LanguageServer::handleTextDocumentDidChange(MessageID _id, Json::Value cons
|
|||||||
compileAndUpdateDiagnostics();
|
compileAndUpdateDiagnostics();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageServer::handleTextDocumentDidClose(MessageID _id, Json::Value const& _args)
|
void LanguageServer::handleTextDocumentDidClose(Json::Value const& _args)
|
||||||
{
|
{
|
||||||
if (!checkServerInitialized(_id))
|
requireServerInitialized();
|
||||||
return;
|
|
||||||
|
|
||||||
if (!_args["textDocument"])
|
if (!_args["textDocument"])
|
||||||
m_client.error(_id, ErrorCode::RequestFailed, "Text document parameter missing.");
|
BOOST_THROW_EXCEPTION(
|
||||||
|
RequestError(ErrorCode::RequestFailed) <<
|
||||||
|
errinfo_comment("Text document parameter missing.")
|
||||||
|
);
|
||||||
|
|
||||||
string uri = _args["textDocument"]["uri"].asString();
|
string uri = _args["textDocument"]["uri"].asString();
|
||||||
m_openFiles.erase(uri);
|
m_openFiles.erase(uri);
|
||||||
|
@ -60,12 +60,12 @@ public:
|
|||||||
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.
|
||||||
bool checkServerInitialized(MessageID _id);
|
void requireServerInitialized();
|
||||||
void handleInitialize(MessageID _id, Json::Value const& _args);
|
void handleInitialize(MessageID _id, Json::Value const& _args);
|
||||||
void handleWorkspaceDidChangeConfiguration(MessageID _id, Json::Value const& _args);
|
void handleWorkspaceDidChangeConfiguration(Json::Value const& _args);
|
||||||
void handleTextDocumentDidOpen(MessageID _id, Json::Value const& _args);
|
void handleTextDocumentDidOpen(Json::Value const& _args);
|
||||||
void handleTextDocumentDidChange(MessageID _id, Json::Value const& _args);
|
void handleTextDocumentDidChange(Json::Value const& _args);
|
||||||
void handleTextDocumentDidClose(MessageID _id, Json::Value const& _args);
|
void handleTextDocumentDidClose(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&);
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolutil/Exceptions.h>
|
||||||
|
|
||||||
#include <json/value.h>
|
#include <json/value.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -45,6 +47,23 @@ enum class ErrorCode
|
|||||||
RequestFailed = -32803
|
RequestFailed = -32803
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error exception used to bail out on errors in the LSP function-call handlers.
|
||||||
|
*/
|
||||||
|
class RequestError: public util::Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit RequestError(ErrorCode _code):
|
||||||
|
m_code{_code}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode code() const noexcept { return m_code; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorCode m_code;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transport layer API
|
* Transport layer API
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user