Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Language Server #11350

Merged
merged 1 commit into from
Dec 16, 2021
Merged

Language Server #11350

merged 1 commit into from
Dec 16, 2021

Conversation

christianparpart
Copy link
Member

@christianparpart christianparpart commented May 5, 2021

This PR contains the Language Server mode for solc. Closes #7763.

Patch for LSP TCP mode saved here.

Copy link
Contributor

@chriseth chriseth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a rough review, but we are getting there indeed!

libsolidity/lsp/LanguageServer.cpp Outdated Show resolved Hide resolved
libsolidity/lsp/LanguageServer.cpp Outdated Show resolved Hide resolved
libsolidity/lsp/LanguageServer.cpp Outdated Show resolved Hide resolved
libsolidity/lsp/LanguageServer.h Outdated Show resolved Hide resolved
libsolidity/lsp/LanguageServer.h Outdated Show resolved Hide resolved
libsolidity/lsp/LanguageServer.cpp Outdated Show resolved Hide resolved
libsolidity/lsp/LanguageServer.cpp Outdated Show resolved Hide resolved
libsolidity/lsp/LanguageServer.cpp Outdated Show resolved Hide resolved
libsolidity/interface/FileReader.cpp Outdated Show resolved Hide resolved
libsolidity/lsp/LanguageServer.cpp Outdated Show resolved Hide resolved
solc/CMakeLists.txt Outdated Show resolved Hide resolved
@christianparpart

This comment has been minimized.

libsolidity/interface/FileReader.cpp Outdated Show resolved Hide resolved
libsolidity/interface/FileReader.cpp Outdated Show resolved Hide resolved
libsolidity/interface/StandardCompiler.cpp Outdated Show resolved Hide resolved
solc/CMakeLists.txt Outdated Show resolved Hide resolved
solc/CMakeLists.txt Outdated Show resolved Hide resolved
solc/CommandLineInterface.cpp Outdated Show resolved Hide resolved
solc/CommandLineInterface.cpp Outdated Show resolved Hide resolved
solc/CommandLineInterface.cpp Outdated Show resolved Hide resolved
@chriseth chriseth force-pushed the lsp branch 3 times, most recently from 0cb3403 to f6e23cc Compare October 14, 2021 16:31
solc/CommandLineParser.cpp Outdated Show resolved Hide resolved
solc/CommandLineParser.cpp Show resolved Hide resolved
f" Assertions: {self.assertions_passed} passed, {self.assertions_failed} failed\n"
)

return min(self.assertions_failed, 127)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also return nonzero if we have at least one failed test, regardless of the number of failed assertions.

Prepares the solc LSP server by calling `initialize`,
and `initialized` methods.
"""
project_root_uri = "file://" + self.project_root_dir
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplication

'rootPath': self.project_root_dir,
'rootUri': project_root_uri,
'trace': 'off',
'workspaceFolders': [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't support this.

if expose_project_root == False:
params['rootUri'] = None
params['rootPath'] = None
lsp.call_method('initialize', params)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this check the response?

code = message['error']["code"]
text = message['error']['message']
raise RuntimeError(f"Error {code} received. {text}")
if not 'method' in message.keys():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if not 'method' in message.keys():
if 'method' not in message.keys():

?


self.expect_equal(len(published_diagnostics), 2, "Diagnostic reports for 2 files")

# primary file:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the order specified?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order is mandated by solc.

CMakeLists.txt Outdated Show resolved Hide resolved
@@ -335,15 +335,24 @@ def expect_equal(self, actual, expected, description="Equality") -> None:
print(prefix + SGR_STATUS_FAIL + 'FAILED' + SGR_RESET)
raise ExpectationFailed(actual, expected)

def expect_diagnostic(self, diagnostic, code: int, lineNo: int, startEndColumns: Tuple[int, int]):
def expect_diagnostic(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a future version we probably want to have factory functions for ranges.

.circleci/config.yml Outdated Show resolved Hide resolved
libsolidity/lsp/FileRepository.h Outdated Show resolved Hide resolved
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change the pragmas in these tests to pragma solidity *?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it matter? A change here will require updating all the locations in the expectations...

Copy link
Member

@cameel cameel Dec 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not so much that it would stop the PR from being merged.

I just think that we won't be keeping them up to date so they will be useless at best and at worst can get in the way in case you want to extract test cases from an older version. The compiler version is implied by the tag on the revision you're taking the code from anyway.


string name = boost::to_lower_copy(line.substr(0, delimiterPos));
string value = line.substr(delimiterPos + 1);
headers[boost::trim_copy(name)] = boost::trim_copy(value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be a good idea to at least add a comment in the source that the problem exists and we're deliberately ignoring it. No one will find this later in the PR :)

libsolidity/lsp/LanguageServer.cpp Outdated Show resolved Hide resolved
libsolidity/lsp/LanguageServer.cpp Outdated Show resolved Hide resolved
Comment on lines +326 to +324
if (!checkServerInitialized(_id))
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably not something to change right now but it strikes me that we would have been having an exception you can throw to report an error to the client. We have this big try block in run(). It could have a catch clause that catches the exception and calls error() with the message+code and continues. The calls to checkServerInitialized() would be much more natural - right now it looks as if it was only checking a condition but it actually immediately reports an error which was unexpected to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually not so sure about it. In the compiler this makes total sense since the control-flow is not that important, but here you might want to know if it gets further down where you answer the request or not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That can be conveyed with naming. For example I'd usually name such a function with validate...() and by the fact that it does not return anything I'd naturally expect it to handle failed validation by throwing.

And you always have the option to use error() manually and not return for more fine-grained control. But for the most common case where you just want to error out and exit I think that exceptions actually make control flow clearer - you can see that the throw will just end the handler however deep you are and you don't have to worry about whether the caller will ignore your return code.

from typing import Callable, List, Union, Any
from deepdiff import DeepDiff

# {{{ JsonRpcProcess
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think it should have been a separate module, though maybe it's too late to change that for the MVP now.

Comment on lines 108 to 114
SGR_RESET = '\033[m'
SGR_TRACE = '\033[1;36m'
SGR_NOTICE = '\033[1;35m'
SGR_TEST_BEGIN = '\033[1;33m'
SGR_ASSERT_BEGIN = '\033[1;34m'
SGR_STATUS_OKAY = '\033[1;32m'
SGR_STATUS_FAIL = '\033[1;31m'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could now replace these with Fore, Back and Style constants from colorama. That was part of the reason to switch to it after all.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason to use colorama for me was to actually make this compatible with Windows platform where no VT processing (Windows below version 10 (18H-something) was available. One could use names here, but It's more then foreground, the 1 means "bold", actually :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will all be removed when we move to pytest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason to use colorama for me was to actually make this compatible with Windows platform

I mean this was another reason but for me it's also important that using colors is simple - I'd like to start using them more in our Python scripts and having to always repeat such a block of definitions would be too annoying and error-prone.

One could use names here, but It's more then foreground, the 1 means "bold", actually :)

Oh, right. I just noticed that your codes are semantic and not just simple color definitions. I guess it should still be possible to somehow map them to combinations of constants from colorama?

This will all be removed when we move to pytest.

True. This can be refactored while we move to a test framework.

But you mean unittest of course? pytest is a completely different test framework and I personally don't like it that much (too much magic :)).

scripts/test_solidity_lsp.py Outdated Show resolved Hide resolved
/**
* LSP Transport using JSON-RPC over iostreams.
*/
class JSONTransport: public Transport
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like #11350 (comment) has not been addressed.

I really think that this being named JSONTransport is confusing. Both it and Transport use JSON. The difference is really that the former uses iostreams and is a concrete implementation while the latter is an interface. I think that the names do not reflect that at all.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON objects, I agree, and this one is about is a JSON (RPC) transport. So Transport itself is just the interface. So the difference is, that this class is actually implementing this interface with respect to JSON-RPC. :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't JSON RPC already an assumption for Transport? Is it even meant to work with anything other than JSON RPC?

In any case, I'd include at least RPC in the name.

colorama.init()
args = create_cli_parser().parse_args()
self.solc_path = args.solc_path
self.project_root_dir = os.path.realpath(args.project_root_dir) + "/test/libsolidity/lsp"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing I mentioned in #11350 (comment) is still relevant here:

Seems a bit misleading to call the lsp dir "project dir". If I saw it, I'd assume it it was the main project dir.

Suggested change
self.project_root_dir = os.path.realpath(args.project_root_dir) + "/test/libsolidity/lsp"
self.lsp_test_dir = os.path.realpath(args.project_root_dir) + "/test/libsolidity/lsp"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is all about lsp, i'm not sure lsp_test_dir is actually anything better here, because the reason it is named project_root_dir is, because that's what is passed as project root dir to the LSP server.

Copy link
Member

@cameel cameel Dec 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@christianparpart clarified that "project" here means the client's project that sources belong to, and not Solidity as a project. This was not obvious to me at all because I deal with our scripts a lot and the first thing that comes to mind when I see "project root" is our repo root. I still think it should be renamed because it's too ambiguous. How about this?

Suggested change
self.project_root_dir = os.path.realpath(args.project_root_dir) + "/test/libsolidity/lsp"
self.lsp_project_dir = os.path.realpath(args.solidity_repo_root) + "/test/libsolidity/lsp"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_workspace_dir?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If workspace is synonymous with project for LSP (I'm not sure about the terminology) then yeah, that's much better.

cameel
cameel previously approved these changes Dec 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement LSP / Language Server
6 participants