Skip to content

Commit 1035eac

Browse files
LSP: Introduce HandlerBase for future LSP-feature implementations.
1 parent 60463cf commit 1035eac

File tree

7 files changed

+249
-75
lines changed

7 files changed

+249
-75
lines changed

libsolidity/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,14 @@ set(sources
159159
lsp/LanguageServer.h
160160
lsp/FileRepository.cpp
161161
lsp/FileRepository.h
162+
lsp/HandlerBase.cpp
163+
lsp/HandlerBase.h
164+
lsp/LanguageServer.cpp
165+
lsp/LanguageServer.h
162166
lsp/Transport.cpp
163167
lsp/Transport.h
168+
lsp/Utils.cpp
169+
lsp/Utils.h
164170
parsing/DocStringParser.cpp
165171
parsing/DocStringParser.h
166172
parsing/Parser.cpp

libsolidity/lsp/HandlerBase.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include <libsolidity/lsp/HandlerBase.h>
2+
#include <libsolidity/lsp/LanguageServer.h>
3+
#include <libsolidity/lsp/Utils.h>
4+
#include <libsolidity/ast/AST.h>
5+
6+
#include <liblangutil/Exceptions.h>
7+
8+
using namespace std;
9+
10+
namespace solidity::lsp
11+
{
12+
13+
using namespace langutil;
14+
15+
Json::Value HandlerBase::toRange(SourceLocation const& _location) const
16+
{
17+
if (!_location.hasText())
18+
return toJsonRange({}, {});
19+
20+
solAssert(_location.sourceName, "");
21+
langutil::CharStream const& stream = charStreamProvider().charStream(*_location.sourceName);
22+
LineColumn start = stream.translatePositionToLineColumn(_location.start);
23+
LineColumn end = stream.translatePositionToLineColumn(_location.end);
24+
return toJsonRange(start, end);
25+
}
26+
27+
Json::Value HandlerBase::toJson(SourceLocation const& _location) const
28+
{
29+
solAssert(_location.sourceName);
30+
Json::Value item = Json::objectValue;
31+
item["uri"] = fileRepository().sourceUnitNameToClientPath(*_location.sourceName);
32+
item["range"] = toRange(_location);
33+
return item;
34+
}
35+
36+
optional<SourceLocation> HandlerBase::parsePosition(string const& _sourceUnitName, Json::Value const& _position) const
37+
{
38+
if (!fileRepository().sourceUnits().count(_sourceUnitName))
39+
return nullopt;
40+
41+
if (optional<LineColumn> lineColumn = parseLineColumn(_position))
42+
if (optional<int> const offset = CharStream::translateLineColumnToPosition(
43+
fileRepository().sourceUnits().at(_sourceUnitName),
44+
*lineColumn
45+
))
46+
return SourceLocation{*offset, *offset, make_shared<string>(_sourceUnitName)};
47+
return nullopt;
48+
}
49+
50+
optional<SourceLocation> HandlerBase::parseRange(string const& _sourceUnitName, Json::Value const& _range) const
51+
{
52+
if (!_range.isObject())
53+
return nullopt;
54+
optional<SourceLocation> start = parsePosition(_sourceUnitName, _range["start"]);
55+
optional<SourceLocation> end = parsePosition(_sourceUnitName, _range["end"]);
56+
if (!start || !end)
57+
return nullopt;
58+
solAssert(*start->sourceName == *end->sourceName);
59+
start->end = end->end;
60+
return start;
61+
}
62+
63+
}

libsolidity/lsp/HandlerBase.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#pragma once
2+
3+
#include <libsolidity/lsp/FileRepository.h>
4+
#include <libsolidity/lsp/LanguageServer.h>
5+
6+
#include <liblangutil/SourceLocation.h>
7+
#include <liblangutil/CharStreamProvider.h>
8+
9+
#include <optional>
10+
11+
namespace solidity::lsp
12+
{
13+
14+
class Transport;
15+
16+
/**
17+
* Helper base class for implementing handlers.
18+
*/
19+
class HandlerBase
20+
{
21+
public:
22+
explicit HandlerBase(LanguageServer& _server): m_server{_server} {}
23+
24+
Json::Value toRange(langutil::SourceLocation const& _location) const;
25+
Json::Value toJson(langutil::SourceLocation const& _location) const;
26+
27+
std::optional<langutil::SourceLocation> parsePosition(
28+
std::string const& _sourceUnitName,
29+
Json::Value const& _position
30+
) const;
31+
32+
/// @returns the source location given a source unit name and an LSP Range object,
33+
/// or nullopt on failure.
34+
std::optional<langutil::SourceLocation> parseRange(
35+
std::string const& _sourceUnitName,
36+
Json::Value const& _range
37+
) const;
38+
39+
langutil::CharStreamProvider const& charStreamProvider() const noexcept { return m_server.charStreamProvider(); };
40+
FileRepository const& fileRepository() const noexcept { return m_server.fileRepository(); };
41+
Transport& client() const noexcept { return m_server.client(); };
42+
43+
LanguageServer& m_server;
44+
};
45+
46+
}

libsolidity/lsp/LanguageServer.cpp

Lines changed: 26 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
#include <libsolidity/interface/ReadFile.h>
2222
#include <libsolidity/interface/StandardCompiler.h>
2323
#include <libsolidity/lsp/LanguageServer.h>
24+
#include <libsolidity/lsp/HandlerBase.h>
25+
#include <libsolidity/lsp/Utils.h>
26+
2427

2528
#include <liblangutil/SourceReferenceExtractor.h>
2629
#include <liblangutil/CharStream.h>
@@ -48,31 +51,6 @@ using namespace solidity::frontend;
4851
namespace
4952
{
5053

51-
Json::Value toJson(LineColumn _pos)
52-
{
53-
Json::Value json = Json::objectValue;
54-
json["line"] = max(_pos.line, 0);
55-
json["character"] = max(_pos.column, 0);
56-
57-
return json;
58-
}
59-
60-
Json::Value toJsonRange(LineColumn const& _start, LineColumn const& _end)
61-
{
62-
Json::Value json;
63-
json["start"] = toJson(_start);
64-
json["end"] = toJson(_end);
65-
return json;
66-
}
67-
68-
optional<LineColumn> parseLineColumn(Json::Value const& _lineColumn)
69-
{
70-
if (_lineColumn.isObject() && _lineColumn["line"].isInt() && _lineColumn["character"].isInt())
71-
return LineColumn{_lineColumn["line"].asInt(), _lineColumn["character"].asInt()};
72-
else
73-
return nullopt;
74-
}
75-
7654
int toDiagnosticSeverity(Error::Type _errorType)
7755
{
7856
// 1=Error, 2=Warning, 3=Info, 4=Hint
@@ -107,55 +85,19 @@ LanguageServer::LanguageServer(Transport& _transport):
10785
{
10886
}
10987

110-
optional<SourceLocation> LanguageServer::parsePosition(
111-
string const& _sourceUnitName,
112-
Json::Value const& _position
113-
) const
88+
optional<SourceLocation> LanguageServer::parseRange(string const& _sourceUnitName, Json::Value const& _range)
11489
{
115-
if (!m_fileRepository.sourceUnits().count(_sourceUnitName))
116-
return nullopt;
117-
118-
if (optional<LineColumn> lineColumn = parseLineColumn(_position))
119-
if (optional<int> const offset = CharStream::translateLineColumnToPosition(
120-
m_fileRepository.sourceUnits().at(_sourceUnitName),
121-
*lineColumn
122-
))
123-
return SourceLocation{*offset, *offset, make_shared<string>(_sourceUnitName)};
124-
return nullopt;
90+
return HandlerBase{*this}.parseRange(_sourceUnitName, _range);
12591
}
12692

127-
optional<SourceLocation> LanguageServer::parseRange(string const& _sourceUnitName, Json::Value const& _range) const
93+
Json::Value LanguageServer::toRange(SourceLocation const& _location)
12894
{
129-
if (!_range.isObject())
130-
return nullopt;
131-
optional<SourceLocation> start = parsePosition(_sourceUnitName, _range["start"]);
132-
optional<SourceLocation> end = parsePosition(_sourceUnitName, _range["end"]);
133-
if (!start || !end)
134-
return nullopt;
135-
solAssert(*start->sourceName == *end->sourceName);
136-
start->end = end->end;
137-
return start;
95+
return HandlerBase(*this).toRange(_location);
13896
}
13997

140-
Json::Value LanguageServer::toRange(SourceLocation const& _location) const
98+
Json::Value LanguageServer::toJson(SourceLocation const& _location)
14199
{
142-
if (!_location.hasText())
143-
return toJsonRange({}, {});
144-
145-
solAssert(_location.sourceName, "");
146-
CharStream const& stream = m_compilerStack.charStream(*_location.sourceName);
147-
LineColumn start = stream.translatePositionToLineColumn(_location.start);
148-
LineColumn end = stream.translatePositionToLineColumn(_location.end);
149-
return toJsonRange(start, end);
150-
}
151-
152-
Json::Value LanguageServer::toJson(SourceLocation const& _location) const
153-
{
154-
solAssert(_location.sourceName);
155-
Json::Value item = Json::objectValue;
156-
item["uri"] = m_fileRepository.sourceUnitNameToClientPath(*_location.sourceName);
157-
item["range"] = toRange(_location);
158-
return item;
100+
return HandlerBase(*this).toJson(_location);
159101
}
160102

161103
void LanguageServer::changeConfiguration(Json::Value const& _settings)
@@ -403,3 +345,20 @@ void LanguageServer::handleTextDocumentDidClose(Json::Value const& _args)
403345

404346
compileAndUpdateDiagnostics();
405347
}
348+
349+
ASTNode const* LanguageServer::requestASTNode(std::string const& _sourceUnitName, LineColumn const& _filePos)
350+
{
351+
if (m_compilerStack.state() < CompilerStack::AnalysisPerformed)
352+
return nullptr;
353+
354+
if (!m_fileRepository.sourceUnits().count(_sourceUnitName))
355+
return nullptr;
356+
357+
optional<int> sourcePos = m_compilerStack.charStream(_sourceUnitName)
358+
.translateLineColumnToPosition(_filePos);
359+
if (!sourcePos.has_value())
360+
return nullptr;
361+
362+
return locateInnermostASTNode(*sourcePos, m_compilerStack.ast(_sourceUnitName));
363+
}
364+

libsolidity/lsp/LanguageServer.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ class LanguageServer
5757
/// @return boolean indicating normal or abnormal termination.
5858
bool run();
5959

60+
FileRepository& fileRepository() noexcept { return m_fileRepository; }
61+
Transport& client() noexcept { return m_client; }
62+
frontend::ASTNode const* requestASTNode(std::string const& _sourceUnitName, langutil::LineColumn const& _filePos);
63+
langutil::CharStreamProvider const& charStreamProvider() const noexcept { return m_compilerStack; }
64+
6065
private:
6166
/// Checks if the server is initialized (to be used by messages that need it to be initialized).
6267
/// Reports an error and returns false if not.
@@ -72,22 +77,18 @@ class LanguageServer
7277

7378
/// Compile everything until after analysis phase.
7479
void compile();
80+
using MessageHandler = std::function<void(MessageID, Json::Value const&)>;
7581

76-
std::optional<langutil::SourceLocation> parsePosition(
77-
std::string const& _sourceUnitName,
78-
Json::Value const& _position
79-
) const;
8082
/// @returns the source location given a source unit name and an LSP Range object,
8183
/// or nullopt on failure.
8284
std::optional<langutil::SourceLocation> parseRange(
8385
std::string const& _sourceUnitName,
8486
Json::Value const& _range
85-
) const;
86-
Json::Value toRange(langutil::SourceLocation const& _location) const;
87-
Json::Value toJson(langutil::SourceLocation const& _location) const;
87+
);
88+
Json::Value toRange(langutil::SourceLocation const& _location);
89+
Json::Value toJson(langutil::SourceLocation const& _location);
8890

8991
// LSP related member fields
90-
using MessageHandler = std::function<void(MessageID, Json::Value const&)>;
9192

9293
enum class State { Started, Initialized, ShutdownRequested, ExitRequested, ExitWithoutShutdown };
9394
State m_state = State::Started;

libsolidity/lsp/Utils.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include <liblangutil/CharStreamProvider.h>
2+
#include <liblangutil/Exceptions.h>
3+
#include <libsolidity/ast/AST.h>
4+
#include <libsolidity/lsp/FileRepository.h>
5+
#include <libsolidity/lsp/Utils.h>
6+
7+
namespace solidity::lsp
8+
{
9+
10+
using namespace frontend;
11+
using namespace langutil;
12+
using namespace std;
13+
14+
optional<LineColumn> parseLineColumn(Json::Value const& _lineColumn)
15+
{
16+
if (_lineColumn.isObject() && _lineColumn["line"].isInt() && _lineColumn["character"].isInt())
17+
return LineColumn{_lineColumn["line"].asInt(), _lineColumn["character"].asInt()};
18+
else
19+
return nullopt;
20+
}
21+
22+
Json::Value toJson(LineColumn _pos)
23+
{
24+
Json::Value json = Json::objectValue;
25+
json["line"] = max(_pos.line, 0);
26+
json["character"] = max(_pos.column, 0);
27+
28+
return json;
29+
}
30+
31+
Json::Value toJsonRange(LineColumn const& _start, LineColumn const& _end)
32+
{
33+
Json::Value json;
34+
json["start"] = toJson(_start);
35+
json["end"] = toJson(_end);
36+
return json;
37+
}
38+
39+
vector<Declaration const*> allAnnotatedDeclarations(Expression const* _expression)
40+
{
41+
vector<Declaration const*> output;
42+
43+
if (auto const* identifier = dynamic_cast<Identifier const*>(_expression))
44+
{
45+
output.push_back(identifier->annotation().referencedDeclaration);
46+
output += identifier->annotation().candidateDeclarations;
47+
}
48+
else if (auto const* memberAccess = dynamic_cast<MemberAccess const*>(_expression))
49+
{
50+
output.push_back(memberAccess->annotation().referencedDeclaration);
51+
}
52+
53+
return output;
54+
}
55+
56+
optional<SourceLocation> declarationPosition(Declaration const* _declaration)
57+
{
58+
if (!_declaration)
59+
return nullopt;
60+
61+
if (_declaration->nameLocation().isValid())
62+
return _declaration->nameLocation();
63+
64+
if (_declaration->location().isValid())
65+
return _declaration->location();
66+
67+
return nullopt;
68+
}
69+
70+
}

libsolidity/lsp/Utils.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#pragma once
2+
3+
#include <liblangutil/SourceLocation.h>
4+
5+
#include <libsolidity/ast/ASTForward.h>
6+
7+
#include <libsolutil/JSON.h>
8+
9+
#include <optional>
10+
#include <vector>
11+
12+
namespace solidity::langutil
13+
{
14+
class CharStreamProvider;
15+
}
16+
17+
namespace solidity::lsp
18+
{
19+
20+
class FileRepository;
21+
22+
std::optional<langutil::LineColumn> parseLineColumn(Json::Value const& _lineColumn);
23+
Json::Value toJson(langutil::LineColumn _pos);
24+
Json::Value toJsonRange(langutil::LineColumn const& _start, langutil::LineColumn const& _end);
25+
26+
std::vector<frontend::Declaration const*> allAnnotatedDeclarations(frontend::Expression const* _expression);
27+
std::optional<langutil::SourceLocation> declarationPosition(frontend::Declaration const* _declaration);
28+
29+
}

0 commit comments

Comments
 (0)