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

Resolve completions completion item before applying additional text edits #1651

Conversation

predragnikolic
Copy link
Member

@predragnikolic predragnikolic commented Apr 29, 2021

fixes sublimelsp/LSP-typescript#46

Short description

This PR introduces resolving the completion item(if session supports resolving) before applying additional text edits and running the epilogue.

output

Longer description

Change 1

The current behavior:

When a LSP.CompletionItem (CI for short) is passed to the format_completion function:

  • if the CI contains a TextEdit,
    it becomes a ST Command Completion Item.
    When that CI is selected in the AC,
    a lsp_complete_text_edit command is run passing in the LSP.CompletionItem and the session_name as arguments.
    So we exactly know what CI was selected in the AC
    and to what session to send the CI to resolve it(only if the session supports resolving completion items).

  • else if the CI does not contains a TextEdit,
    it becomes a ST Snippet Completion Item or a regular ST Completion Item.
    When that CI is selected,
    we don't know what exact completion item was selected or what session it belongs to, because ST.SnippetCompletionItem or ST.CompletionItem function a bit differently that Command Completion Items.

To conclude, IMO I see more benefits using Command Completion Items as they gives us the ability to always know what completion item was selected and what session the CI belongs.

So in this PR:
I change the format_completion to always return a command completion item. That way we always know what completion item was selected, what the session name is, and based on that we can insert TextEdits, or insert the label or newText, resolve completion items and apply additional text edits correctly.

Change 2

Current behavior:
Additional text edits will not be applied if:
a) the unresolved CI don't have additionalTextEdits, but the resolved completion CI has additionalTextEdits . (which was the bug with the theia typescript lanugage server - sublimelsp/LSP-typescript#46)
b) If we select ST Snippet Completion items or regular ST Completion items (no way to detect what exact CI was selected,
and currently there is no logic that applies additionalTextEdits for ST Snippet Completion items or regular ST Completion items.)

Here is how this PR addresses those above mention issues.

format_completion_item returns a Command Completion Item
When we select the Command Completion Item in the AC
a lsp_select_completion_item command is run.
We insert the text in the view(based on if it is a TextEdit, insertText, or label, Snippet or PlainText).
Than we run the epilogue,
where we will try to resolve the CI in the epilogue if the session supports resolving (this is a new change),
else if the session doesn't support resolving,
we will applyAdditionalTextEdits and everything else that was earlier already done in the epilogue.


TODO

  • Fix failing test

There is currently one test that is failing and that is:

FAIL: test_additional_edits (test_completion.QueryCompletionsTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/predrag/.config/sublime-text/Packages/UnitTesting/unittesting/core/py33/case.py", line 28, in _executeTestPart
    yield from deferred
  File "/home/predrag/.config/sublime-text/Packages/LSP/tests/test_completion.py", line 377, in test_additional_edits
    expected_text='import asdf;\nasdf')
  File "/home/predrag/.config/sublime-text/Packages/LSP/tests/test_completion.py", line 76, in verify
    self.assertEqual(self.read_file(), expected_text)
AssertionError: 'asdf' != 'import asdf;\nasdf'
- asdf
+ import asdf;
asdf

The test is failing because of this addition:
If we comment it out the test will pass:

    def epilogue(self, item: CompletionItem, session_name: str) -> None:
        # session = self.session_by_name(session_name, 'completionProvider.resolveProvider')
        # if session:
        #     request = Request.resolveCompletionItem(item, self.view)
        #     session.send_request_async(request, lambda response: self.on_resolved(response, session_name))
        # else:
        self.on_resolved(item, session_name)

My current conclusion of why the test is failing now is that now I added the check in the epilogue to see if session supports resolveProvider. which when running tests will be true, but right now in the test I haven't added a mock response for the resolve completion item request, and thus the test failed.

Feel free to correct me, but I think that the test will pass if:

  • we set the session in the test to not have the resolveCompletionItem capability
  • or set the session to have the resolveCompletionItem capability and add a response for the resolveCompletion item request.

@predragnikolic
Copy link
Member Author

I think I put more effort to explain why I did things that the code itself :D
If the text I wrote is too much, read the code.

plugin/core/views.py Outdated Show resolved Hide resolved
plugin/core/sessions.py Outdated Show resolved Hide resolved
plugin/completion.py Outdated Show resolved Hide resolved
tests/test_completion.py Outdated Show resolved Hide resolved
predragnikolic and others added 2 commits April 29, 2021 21:55
Co-authored-by: Rafał Chłodnicki <rchl2k@gmail.com>
boot.py Outdated Show resolved Hide resolved
@predragnikolic
Copy link
Member Author

predragnikolic commented Apr 29, 2021

I noticed one thing that I wasn't expecting.

I moved the additional text edits from the completion item to the resolved completion item. And the test started to fail... I was not expecting that.

 def test_additional_edits_if_session_has_the_resolve_capability(self) -> 'Generator':
        completion_item = {
            'label': 'asdf'
-            'additionalTextEdits': [
-                {
-                    'range': {
-                        'start': {
-                            'line': 0,
-                            'character': 0
-                        },
-                        'end': {
-                            'line': 0,
-                            'character': 0
-                        }
-                    },
-                    'newText': 'import asdf;\n'
-                }
-            ]
        }
        self.set_response("completionItem/resolve", {
+            'label': 'asdf',
+            'additionalTextEdits': [
+                {
+                    'range': {
+                        'start': {
+                            'line': 0,
+                            'character': 0
+                        },
+                        'end': {
+                            'line': 0,
+                            'character': 0
+                        }
+                    },
+                    'newText': 'import asdf;\n'
+                }
+            ]
        })
        yield from self.verify(
            completion_items=[completion_item],
            insert_text='',
            expected_text='import asdf;\nasdf')

I am not sure why the test is failing.

EDIT:
The QueryCompletionsNoResolverTests.get_test_server_capabilities is making the above QueryCompletionsTests.test_additional_edits_if_session_has_the_resolve_capability to fail. Commenting out the QueryCompletionsNoResolverTests.get_test_server_capabilities will fix the test... But I am not sure why/how the other class QueryCompletionsNoResolverTests can affects the QueryCompletionsTests


SOLVED with b9ba18a

plugin/completion.py Outdated Show resolved Hide resolved
plugin/completion.py Outdated Show resolved Hide resolved
rchl
rchl previously approved these changes Apr 30, 2021
the QueryCompletionsNoResolverTests should not directly extend the QueryCompletionsTests class 
because then the QueryCompletionsNoResolverTests.get_test_server_capabilities will affect the 
QueryCompletionsTests.test_additional_edits_if_session_has_the_resolve_capability and make it fail.

Instead extract all Completion helpers to a separate class and extend them in both QueryCompletionsTests and QueryCompletionsNoResolverTests classes
plugin/completion.py Show resolved Hide resolved
completion_item = {
'label': 'asdf'
}
self.set_response("completionItem/resolve", {
Copy link
Member

Choose a reason for hiding this comment

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

Nice tests here :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks

@rwols rwols merged commit 988cd02 into st4000-exploration May 1, 2021
@rwols rwols deleted the try-to-resolve-completion-items-before-applying-additional-text-edits branch May 1, 2021 10:23
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.

Autocomplete imports as you type?
4 participants