Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions spyder/plugins/completion/kite/providers/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ def request_document_completions(self, params):

@handles(LSPRequestTypes.DOCUMENT_COMPLETION)
def convert_completion_request(self, response):
# The response schema is tested via mocking in
# spyder/plugins/editor/widgets/tests/test_introspection.py

logger.debug(response)
if response is None:
return {'params': []}
Expand Down
3 changes: 2 additions & 1 deletion spyder/plugins/editor/widgets/codeeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,8 @@ def process_completion(self, params):
completions = params['params']
completions = ([] if completions is None else
[completion for completion in completions
if completion['insertText']])
if completion.get('insertText')
or completion.get('textEdit', {}).get('newText')])

replace_end = self.textCursor().position()
under_cursor = self.get_current_word_and_position(completion=True)
Expand Down
31 changes: 31 additions & 0 deletions spyder/plugins/editor/widgets/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,37 @@ def teardown():
return completions


@pytest.fixture
def mock_codeeditor(qtbot_module, request):
Comment thread
metalogical marked this conversation as resolved.
Outdated
"""CodeEditor instance with ability to mock the completions response"""
# Create a CodeEditor instance
editor = codeeditor_factory()
qtbot_module.addWidget(editor)
editor.show()

mock = Mock()
Comment thread
metalogical marked this conversation as resolved.
Outdated
mock.side_effect = lambda *args: None
Comment thread
metalogical marked this conversation as resolved.
Outdated

def perform_request(lang, method, params):
resp = mock(lang, method, params)
print("DEBUG {}".format(resp))
if resp is not None:
editor.handle_response(method, resp)
editor.sig_perform_completion_request.connect(perform_request)

editor.filename = 'test.py'
editor.language = 'Python'
editor.completions_available = True
qtbot_module.wait(2000)

def teardown():
editor.hide()
editor.completion_widget.hide()
request.addfinalizer(teardown)

return editor, mock


@pytest.fixture
def lsp_codeeditor(lsp_plugin, qtbot_module, request):
"""CodeEditor instance with LSP services activated."""
Expand Down
48 changes: 48 additions & 0 deletions spyder/plugins/editor/widgets/tests/test_introspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
rtree_available = False

# Local imports
from spyder.plugins.completion.languageserver import (
LSPRequestTypes, CompletionItemKind)
from spyder.plugins.completion.kite.providers.document import KITE_COMPLETION
from spyder.py3compat import PY2
from spyder.config.manager import CONF

Expand Down Expand Up @@ -764,6 +767,51 @@ def test_fallback_completions(fallback_codeeditor, qtbot):
code_editor.toggle_code_snippets(True)


@pytest.mark.slow
@pytest.mark.first
@flaky(max_runs=5)
def test_kite_textEdit_completions(mock_codeeditor, qtbot):
Comment thread
metalogical marked this conversation as resolved.
Outdated
"""Test on-the-fly completions."""
Comment thread
metalogical marked this conversation as resolved.
Outdated
code_editor, mock = mock_codeeditor
Comment thread
metalogical marked this conversation as resolved.
Outdated
completion = code_editor.completion_widget

code_editor.toggle_automatic_completions(False)
code_editor.toggle_code_snippets(False)

# Set cursor to start
code_editor.go_to_line(1)

qtbot.keyClicks(code_editor, 'my_dict.')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why the . at end of my_dict is necessary here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The reason I use this test case is because this is a type of completion that Kite returns (it allows users to quickly access dict keys with a .), and it's a case where the completion ["dict-key"] replacing the . can't be represented with just insertText.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ok, I didn't know that and it's pretty cool!


# Complete f -> from
Comment thread
metalogical marked this conversation as resolved.
Outdated
mock.side_effect = lambda lang, method, params: {'params': [{
'kind': CompletionItemKind.TEXT,
'label': '["dict-key"]',
'textEdit': {
'newText': '["dict-key"]',
'range': {
'start': 7,
'end': 8,
},
},
'filterText': '',
'sortText': '',
'documentation': '',
'provider': KITE_COMPLETION,
}]} if method == LSPRequestTypes.DOCUMENT_COMPLETION else None
with qtbot.waitSignal(completion.sig_show_completions,
timeout=10000) as sig:
qtbot.keyPress(code_editor, Qt.Key_Tab, delay=300)
mock.side_effect = None

assert '["dict-key"]' in [x['label'] for x in sig.args[0]]
qtbot.keyPress(code_editor, Qt.Key_Enter, delay=300)
assert code_editor.toPlainText() == 'my_dict["dict-key"]\n'

code_editor.toggle_automatic_completions(True)
code_editor.toggle_code_snippets(True)


if __name__ == '__main__':
pytest.main(['test_introspection.py', '--run-slow'])

Expand Down