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

Use language server implementation instead of language for URLs #199

Merged
merged 18 commits into from
Feb 22, 2020

Conversation

bollwyvl
Copy link
Collaborator

@bollwyvl bollwyvl commented Feb 15, 2020

References

Code changes

  • backend

    • uses implementation name for websocket key, e.g. /lsp/ws/pyls
    • moves status to /lsp/status
    • move reading/writing to stdio language servers to threads
    • move reading/writing to shadow files to threads
  • fix frontend

    • introduce ILanguageServerManager
    • use ILSM in ConnectionManager and StatusBarItem

User-facing changes

  • None, other than launching a document with a language already supported by another language will be faster, as there's only one actual WebSocket/process behind them

Backwards-incompatible changes

  • bumps schema to version 2
    • changes session payload to be a dict, e.g. {"session": {"pyls": {}}}

Chores

  • linted
  • tested
  • documented
  • changelog entry

@bollwyvl
Copy link
Collaborator Author

bollwyvl commented Feb 15, 2020

starting point for getting the frontend to compile:

../jupyterlab-lsp/src/adapters/jupyterlab/components/statusbar.tsx(341,7): error TS2740: Type '{ [k: string]: LanguageServerSession; }' is missing the following properties from type 'LanguageServerSession[]': length, pop, push, concat, and 26 more.

here we go...

"typescript-jsx",
"typescript",
"yaml",
KNOWN_SERVERS = [
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

nice thing here is it is now slightly faster...

@bollwyvl bollwyvl changed the title [wip] use language server implementation instead of language for URLs Use language server implementation instead of language for URLs Feb 18, 2020
@bollwyvl
Copy link
Collaborator Author

Turns out I didn't have to do #190, so I didn't. Otherwise seems pretty good!

@@ -19,7 +19,7 @@ CSS

Docker
${def} = Set Variable xpath://span[contains(@class, 'cm-string')][contains(text(), 'PLANET')]
Editor Shows Features for Language Docker Dockerfile Diagnostics=Instruction has no arguments Jump to Definition=${def} Rename=${def}
Wait Until Keyword Succeeds 3x 100ms Editor Shows Features for Language Docker Dockerfile Diagnostics=Instruction has no arguments Jump to Definition=${def} Rename=${def}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So this "fixes" the problem, but the fact remains that you don't reliably get diagnostics back from a dockerfile on the first file opened until you change the file (then it seems to work every time). I tried sending more change events, but it didn't have an effect. I'm willing to deal with it if it makes the tests pass for now.

@@ -34,6 +40,7 @@ def __repr__(self): # pragma: no cover
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.log.debug("%s initialized", self)
self.executor = ThreadPoolExecutor(max_workers=1)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought all of the jiggery pokery i did with the streams was sufficient to handle this, but I've still hit some slowdown. This should Make Sure we don't block the main thread on IO below

@@ -87,7 +94,7 @@ def wake(self):
else:
self.wake()

await self.queue.put(message)
IOLoop.current().add_callback(self.queue.put_nowait, message)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

again, I don't think this was causing issues, but better be safe...

@@ -98,12 +105,12 @@ def wake(self):
message = ""
headers = HTTPHeaders()

line = self._readline()
line = await convert_yielded(self._readline())
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

there are other wrappers that wouldn't require converting, but this works...


@run_on_executor
def _write_one(self, message) -> None:
self.stream.write(message)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

could have moved more string operations here, but keeps the critical region more clear

@bollwyvl
Copy link
Collaborator Author

I'll have a look at fixing the merge conflicts presently...

Copy link
Member

@krassowski krassowski left a comment

Choose a reason for hiding this comment

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

LGTM. I note two debug calls that would be best moved to a console script the future, but they are not a big hurdle right now. Will merge in 2 hours.

@krassowski
Copy link
Member

Ok, I will wait!

@bollwyvl
Copy link
Collaborator Author

Ok, provided docker doesn't flake out on us, this is good to go if CI passes.

@@ -16,11 +23,19 @@ def extract_or_none(obj, path):


class EditableFile:
executor = ThreadPoolExecutor(max_workers=MAX_WORKERS)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

following the same approach as stdio, but in this case we have one executor for all instances of EditableFile. 4 seems like enough for now.

self.lines = await convert_yielded(self.read_lines())

async def write(self):
return await convert_yielded(self.write_lines())
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

these just wrap the underlying tornado yield-style async stuff.

file = EditableFile(path)
editable_file = EditableFile(path)

await editable_file.read()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

renaming is cosmetic, just don't need to go re-defining builtins without a good reason.

It's a little ugly to have to do the read: could add an async class method to EditableFile to do both in one go...

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 a small hill, but file is not a builtin in Python3 and it is ok to use it. It was in Python2, and yes some syntax highlighters did not catch up, but we can use it now.

@bollwyvl
Copy link
Collaborator Author

have definitely still been getting the dockerfile issue locally... very challenging to reproduce, apparently...

@bollwyvl
Copy link
Collaborator Author

bollwyvl commented Feb 22, 2020 via email

@bollwyvl
Copy link
Collaborator Author

I swear I'm done tinkering now.

@krassowski krassowski merged commit f3fd809 into jupyter-lsp:master Feb 22, 2020
@krassowski
Copy link
Member

Now let's hope we will have CI pass on master ;)

@bollwyvl
Copy link
Collaborator Author

bollwyvl commented Feb 22, 2020 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use implementation name rather than language for WebSocket URLs
2 participants