-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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
Language Server #11350
Conversation
There was a problem hiding this 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!
24c65f9
to
a2b5118
Compare
This comment has been minimized.
This comment has been minimized.
0cb3403
to
f6e23cc
Compare
scripts/test_solidity_lsp.py
Outdated
f" Assertions: {self.assertions_passed} passed, {self.assertions_failed} failed\n" | ||
) | ||
|
||
return min(self.assertions_failed, 127) |
There was a problem hiding this comment.
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.
scripts/test_solidity_lsp.py
Outdated
Prepares the solc LSP server by calling `initialize`, | ||
and `initialized` methods. | ||
""" | ||
project_root_uri = "file://" + self.project_root_dir |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplication
scripts/test_solidity_lsp.py
Outdated
'rootPath': self.project_root_dir, | ||
'rootUri': project_root_uri, | ||
'trace': 'off', | ||
'workspaceFolders': [ |
There was a problem hiding this comment.
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.
scripts/test_solidity_lsp.py
Outdated
if expose_project_root == False: | ||
params['rootUri'] = None | ||
params['rootPath'] = None | ||
lsp.call_method('initialize', params) |
There was a problem hiding this comment.
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?
scripts/test_solidity_lsp.py
Outdated
code = message['error']["code"] | ||
text = message['error']['message'] | ||
raise RuntimeError(f"Error {code} received. {text}") | ||
if not 'method' in message.keys(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if not 'method' in message.keys(): | |
if 'method' not in message.keys(): |
?
scripts/test_solidity_lsp.py
Outdated
|
||
self.expect_equal(len(published_diagnostics), 2, "Diagnostic reports for 2 files") | ||
|
||
# primary file: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the order specified?
There was a problem hiding this comment.
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.
scripts/test_solidity_lsp.py
Outdated
@@ -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( |
There was a problem hiding this comment.
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.
@@ -0,0 +1,10 @@ | |||
// SPDX-License-Identifier: UNLICENSED | |||
pragma solidity >=0.8.0; |
There was a problem hiding this comment.
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 *
?
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.
libsolidity/lsp/Transport.cpp
Outdated
|
||
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); |
There was a problem hiding this comment.
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 :)
if (!checkServerInitialized(_id)) | ||
return; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
scripts/test_solidity_lsp.py
Outdated
from typing import Callable, List, Union, Any | ||
from deepdiff import DeepDiff | ||
|
||
# {{{ JsonRpcProcess |
There was a problem hiding this comment.
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.
scripts/test_solidity_lsp.py
Outdated
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' |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 :)).
libsolidity/lsp/Transport.h
Outdated
/** | ||
* LSP Transport using JSON-RPC over iostreams. | ||
*/ | ||
class JSONTransport: public Transport |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. :)
There was a problem hiding this comment.
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.
scripts/test_solidity_lsp.py
Outdated
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" |
There was a problem hiding this comment.
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.
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" |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
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" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test_workspace_dir
?
There was a problem hiding this comment.
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.
This PR contains the Language Server mode for solc. Closes #7763.
Patch for LSP TCP mode saved here.