Skip to content

Commit 193d3b8

Browse files
committed
lsp: support setting remappings on init
support remappings in the lsp server by allowing reading the required configuration during lsp "initialize". the format expected is: ``` { "remappings": [{context: "", prefix: "foo", target: "bar"}] } ```
1 parent 22a0c46 commit 193d3b8

File tree

4 files changed

+99
-6
lines changed

4 files changed

+99
-6
lines changed

libsolidity/lsp/FileRepository.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
// SPDX-License-Identifier: GPL-3.0
1818

19+
#include <libsolidity/interface/ImportRemapper.h>
1920
#include <libsolidity/lsp/FileRepository.h>
2021
#include <libsolidity/lsp/Transport.h>
2122
#include <libsolidity/lsp/Utils.h>
@@ -43,9 +44,10 @@ using solidity::util::readFileAsString;
4344
using solidity::util::joinHumanReadable;
4445
using solidity::util::Result;
4546

46-
FileRepository::FileRepository(boost::filesystem::path _basePath, std::vector<boost::filesystem::path> _includePaths):
47+
FileRepository::FileRepository(boost::filesystem::path _basePath, std::vector<boost::filesystem::path> _includePaths, vector<frontend::ImportRemapper::Remapping> _remappings):
4748
m_basePath(std::move(_basePath)),
48-
m_includePaths(std::move(_includePaths))
49+
m_includePaths(std::move(_includePaths)),
50+
m_remappings(std::move(_remappings))
4951
{
5052
}
5153

@@ -54,6 +56,11 @@ void FileRepository::setIncludePaths(std::vector<boost::filesystem::path> _paths
5456
m_includePaths = std::move(_paths);
5557
}
5658

59+
void FileRepository::setRemappings(vector<frontend::ImportRemapper::Remapping> _remappings)
60+
{
61+
m_remappings = std::move(_remappings);
62+
}
63+
5764
string FileRepository::sourceUnitNameToUri(string const& _sourceUnitName) const
5865
{
5966
regex const windowsDriveLetterPath("^[a-zA-Z]:/");

libsolidity/lsp/FileRepository.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#pragma once
1919

2020
#include <libsolidity/interface/FileReader.h>
21+
#include <libsolidity/interface/ImportRemapper.h>
2122
#include <libsolutil/Result.h>
2223

2324
#include <string>
@@ -29,11 +30,14 @@ namespace solidity::lsp
2930
class FileRepository
3031
{
3132
public:
32-
FileRepository(boost::filesystem::path _basePath, std::vector<boost::filesystem::path> _includePaths);
33+
FileRepository(boost::filesystem::path _basePath, std::vector<boost::filesystem::path> _includePaths, std::vector<frontend::ImportRemapper::Remapping> _remappings);
3334

3435
std::vector<boost::filesystem::path> const& includePaths() const noexcept { return m_includePaths; }
3536
void setIncludePaths(std::vector<boost::filesystem::path> _paths);
3637

38+
std::vector<frontend::ImportRemapper::Remapping> const& remappings() const noexcept { return m_remappings; }
39+
void setRemappings(std::vector<frontend::ImportRemapper::Remapping> _remappings);
40+
3741
boost::filesystem::path const& basePath() const { return m_basePath; }
3842

3943
/// Translates a compiler-internal source unit name to an LSP client path.
@@ -64,6 +68,9 @@ class FileRepository
6468
/// Additional directories used for resolving relative paths in imports.
6569
std::vector<boost::filesystem::path> m_includePaths;
6670

71+
/// Remappings of imports.
72+
std::vector<frontend::ImportRemapper::Remapping> m_remappings;
73+
6774
/// Mapping of source unit names to their URIs as understood by the client.
6875
StringMap m_sourceUnitNamesToUri;
6976

libsolidity/lsp/LanguageServer.cpp

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <libsolidity/ast/AST.h>
1919
#include <libsolidity/ast/ASTUtils.h>
2020
#include <libsolidity/ast/ASTVisitor.h>
21+
#include <libsolidity/interface/ImportRemapper.h>
2122
#include <libsolidity/interface/ReadFile.h>
2223
#include <libsolidity/interface/StandardCompiler.h>
2324
#include <libsolidity/lsp/LanguageServer.h>
@@ -149,7 +150,7 @@ LanguageServer::LanguageServer(Transport& _transport):
149150
{"textDocument/semanticTokens/full", bind(&LanguageServer::semanticTokensFull, this, _1, _2)},
150151
{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _2)},
151152
},
152-
m_fileRepository("/" /* basePath */, {} /* no search paths */),
153+
m_fileRepository("/" /* basePath */, {} /* no search paths */, {} /* no remappings */),
153154
m_compilerStack{m_fileRepository.reader()}
154155
{
155156
}
@@ -210,6 +211,39 @@ void LanguageServer::changeConfiguration(Json::Value const& _settings)
210211
if (typeFailureCount)
211212
m_client.trace("Invalid JSON configuration passed. \"include-paths\" must be an array of strings.");
212213
}
214+
215+
Json::Value jsonRemappings = _settings["remappings"];
216+
217+
if (jsonRemappings)
218+
{
219+
int typeFailureCount = 0;
220+
if (jsonRemappings.isArray())
221+
{
222+
vector<frontend::ImportRemapper::Remapping> remappings;
223+
for (Json::Value const& jsonPath: jsonRemappings)
224+
{
225+
if (jsonPath.isObject())
226+
{
227+
remappings.emplace_back(ImportRemapper::Remapping{
228+
jsonPath.get("context", "").asString(),
229+
jsonPath.get("prefix", "").asString(),
230+
jsonPath.get("target", "").asString(),
231+
});
232+
}
233+
else
234+
{
235+
typeFailureCount++;
236+
}
237+
}
238+
m_fileRepository.setRemappings(move(remappings));
239+
}
240+
else
241+
++typeFailureCount;
242+
243+
if (typeFailureCount)
244+
m_client.trace("Invalid JSON configuration passed. \"remappings\" should be of form [{\"context\":\"\", \"prefix\":\"foo\", \"target\":\"bar\"}].");
245+
246+
}
213247
}
214248

215249
vector<boost::filesystem::path> LanguageServer::allSolidityFilesFromProject() const
@@ -235,7 +269,7 @@ void LanguageServer::compile()
235269
// For files that are not open, we have to take changes on disk into account,
236270
// so we just remove all non-open files.
237271

238-
FileRepository oldRepository(m_fileRepository.basePath(), m_fileRepository.includePaths());
272+
FileRepository oldRepository(m_fileRepository.basePath(), m_fileRepository.includePaths(), m_fileRepository.remappings());
239273
swap(oldRepository, m_fileRepository);
240274

241275
// Load all solidity files from project.
@@ -259,6 +293,7 @@ void LanguageServer::compile()
259293
// TODO: optimize! do not recompile if nothing has changed (file(s) not flagged dirty).
260294

261295
m_compilerStack.reset(false);
296+
m_compilerStack.setRemappings(m_fileRepository.remappings());
262297
m_compilerStack.setSources(m_fileRepository.sourceUnits());
263298
m_compilerStack.compile(CompilerStack::State::AnalysisPerformed);
264299
}
@@ -402,7 +437,7 @@ void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
402437
if (_args["trace"])
403438
setTrace(_args["trace"]);
404439

405-
m_fileRepository = FileRepository(rootPath, {});
440+
m_fileRepository = FileRepository(rootPath, {}, {});
406441
if (_args["initializationOptions"].isObject())
407442
changeConfiguration(_args["initializationOptions"]);
408443

test/lsp.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,50 @@ def test_custom_includes(self, solc: JsonRpcProcess) -> None:
15341534
self.expect_equal(len(diagnostics), 1, "no diagnostics")
15351535
self.expect_diagnostic(diagnostics[0], code=2018, lineNo=5, startEndColumns=(4, 62))
15361536

1537+
def test_remapping(self, solc: JsonRpcProcess) -> None:
1538+
self.setup_lsp(solc, expose_project_root=False)
1539+
solc.send_notification(
1540+
'workspace/didChangeConfiguration', {
1541+
'settings': {
1542+
'remappings': [
1543+
{
1544+
'context': '',
1545+
'prefix': '@other/',
1546+
'target': f"{self.project_root_dir}/other-include-dir/otherlib/"
1547+
}
1548+
]
1549+
}
1550+
}
1551+
)
1552+
1553+
# test file
1554+
file_with_remapped_import_uri = 'file:///remapped-include.sol'
1555+
solc.send_message('textDocument/didOpen', {
1556+
'textDocument': {
1557+
'uri': file_with_remapped_import_uri,
1558+
'languageId': 'Solidity',
1559+
'version': 1,
1560+
'text': ''.join([
1561+
'// SPDX-License-Identifier: UNLICENSED\n',
1562+
'pragma solidity >=0.8.0;\n',
1563+
'import "@other/otherlib.sol";\n',
1564+
])
1565+
}
1566+
})
1567+
published_diagnostics = self.wait_for_diagnostics(solc)
1568+
self.expect_equal(len(published_diagnostics), 2, "expected reports for two files")
1569+
1570+
report = published_diagnostics[0]
1571+
self.expect_equal(report['uri'], f"{self.project_root_uri}/other-include-dir/otherlib/otherlib.sol")
1572+
diagnostics = report['diagnostics']
1573+
self.expect_equal(len(diagnostics), 1, "no diagnostics")
1574+
self.expect_diagnostic(diagnostics[0], code=2018, lineNo=5, startEndColumns=(4, 62))
1575+
1576+
report = published_diagnostics[1]
1577+
self.expect_equal(report['uri'], file_with_remapped_import_uri)
1578+
diagnostics = report['diagnostics']
1579+
self.expect_equal(len(diagnostics), 0, "no diagnostics")
1580+
15371581
def test_custom_includes_with_full_project(self, solc: JsonRpcProcess) -> None:
15381582
"""
15391583
Tests loading all project files while having custom include directories configured.

0 commit comments

Comments
 (0)