diff --git a/pyls/_utils.py b/pyls/_utils.py index 919bf1c5..7c6261df 100644 --- a/pyls/_utils.py +++ b/pyls/_utils.py @@ -111,6 +111,26 @@ def list_to_string(value): return ",".join(value) if isinstance(value, list) else value +def merge_string(a, b): + if len(a) < len(b): + if b.startswith(a): + return b + + for i in range(len(a)): + if b.startswith(a[i:]): + return a[:i] + b + + else: + if a.endswith(b): + return a + + for i in range(len(b) - 1, -1, -1): + if a.endswith(b[:i]): + return a[:-i] + b + + return a + b + + def merge_dicts(dict_a, dict_b): """Recursively merge dictionary b into dictionary a. diff --git a/pyls/lsp.py b/pyls/lsp.py index 36a8d842..a3f88d38 100644 --- a/pyls/lsp.py +++ b/pyls/lsp.py @@ -24,6 +24,13 @@ class CompletionItemKind(object): Color = 16 File = 17 Reference = 18 + Folder = 19 + EnumMember = 20 + Constant = 21 + Struct = 22 + Event = 23 + Operator = 24 + TypeParameter = 25 class DocumentHighlightKind(object): diff --git a/pyls/plugins/jedi_completion.py b/pyls/plugins/jedi_completion.py index c552e396..e2529e03 100644 --- a/pyls/plugins/jedi_completion.py +++ b/pyls/plugins/jedi_completion.py @@ -31,10 +31,10 @@ 'property': lsp.CompletionItemKind.Property, 'import': lsp.CompletionItemKind.Module, 'keyword': lsp.CompletionItemKind.Keyword, - 'constant': lsp.CompletionItemKind.Variable, + 'constant': lsp.CompletionItemKind.Constant, 'variable': lsp.CompletionItemKind.Variable, 'value': lsp.CompletionItemKind.Value, - 'param': lsp.CompletionItemKind.Variable, + 'param': lsp.CompletionItemKind.TypeParameter, 'statement': lsp.CompletionItemKind.Keyword, } @@ -51,17 +51,27 @@ def pyls_completions(config, document, position): settings = config.plugin_settings('jedi_completion', document_path=document.path) should_include_params = settings.get('include_params') - return [_format_completion(d, snippet_support and should_include_params) for d in definitions] or None + return [_format_completion(d, position, snippet_support and should_include_params) for d in definitions] or None -def _format_completion(d, include_params=True): +def _format_completion(d, position, include_params=True): + insert_text = _utils.merge_string(d.name, d.complete) + start_character = position['character'] + len(d.complete) - len(insert_text) + completion = { 'label': _label(d), 'kind': _TYPE_MAP.get(d.type), 'detail': _detail(d), 'documentation': _utils.format_docstring(d.docstring()), 'sortText': _sort_text(d), - 'insertText': d.name + 'insertText': insert_text, + 'textEdit': { + 'range': { + 'start': {'line': position['line'], 'character': start_character}, + 'end': position, + }, + 'newText': insert_text, + } } if include_params and hasattr(d, 'params') and d.params: @@ -75,7 +85,9 @@ def _format_completion(d, include_params=True): if i < len(positional_args) - 1: snippet += ', ' snippet += ')$0' + completion['insertText'] = snippet + completion['textEdit']['newText'] = snippet return completion @@ -85,6 +97,9 @@ def _label(definition): params = ', '.join([param.name for param in definition.params]) return '{}({})'.format(definition.name, params) + if definition.type == 'param': + return definition.name + '=' + return definition.name diff --git a/pyls/plugins/rope_completion.py b/pyls/plugins/rope_completion.py index e556e464..519b349f 100644 --- a/pyls/plugins/rope_completion.py +++ b/pyls/plugins/rope_completion.py @@ -1,6 +1,6 @@ # Copyright 2017 Palantir Technologies, Inc. import logging -from rope.contrib.codeassist import code_assist, sorted_proposals +from rope.contrib.codeassist import code_assist, sorted_proposals, starting_offset from pyls import hookimpl, lsp @@ -37,6 +37,7 @@ def pyls_completions(config, workspace, document, position): return [] definitions = sorted_proposals(definitions) + start = document.position_at_offset(starting_offset(document.source, offset)) new_definitions = [] for d in definitions: try: @@ -48,7 +49,14 @@ def pyls_completions(config, workspace, document, position): 'kind': _kind(d), 'detail': '{0} {1}'.format(d.scope or "", d.name), 'documentation': doc or "", - 'sortText': _sort_text(d) + 'sortText': _sort_text(d), + 'textEdit': { + 'range': { + 'start': start, + 'end': position, + }, + 'newText': d.name, + } }) definitions = new_definitions @@ -97,10 +105,10 @@ def _kind(d): 'property': lsp.CompletionItemKind.Property, 'import': lsp.CompletionItemKind.Module, 'keyword': lsp.CompletionItemKind.Keyword, - 'constant': lsp.CompletionItemKind.Variable, + 'constant': lsp.CompletionItemKind.Constant, 'variable': lsp.CompletionItemKind.Variable, 'value': lsp.CompletionItemKind.Value, - 'param': lsp.CompletionItemKind.Variable, + 'param': lsp.CompletionItemKind.TypeParameter, 'statement': lsp.CompletionItemKind.Keyword, } diff --git a/pyls/python_ls.py b/pyls/python_ls.py index 9084bc77..c0a21315 100644 --- a/pyls/python_ls.py +++ b/pyls/python_ls.py @@ -296,6 +296,14 @@ def m_text_document__did_open(self, textDocument=None, **_kwargs): self._hook('pyls_document_did_open', textDocument['uri']) self.lint(textDocument['uri'], is_saved=True) + def m_text_document__info(self, textDocument=None, **_kwargs): + workspace = self._match_uri_to_workspace(textDocument['uri']) + doc = workspace.documents[textDocument['uri']] + return { + 'version': doc.version, + 'text': doc.source + } + def m_text_document__did_change(self, contentChanges=None, textDocument=None, **_kwargs): workspace = self._match_uri_to_workspace(textDocument['uri']) for change in contentChanges: diff --git a/pyls/workspace.py b/pyls/workspace.py index 0ffab25a..0095fbd3 100644 --- a/pyls/workspace.py +++ b/pyls/workspace.py @@ -174,6 +174,14 @@ def apply_change(self, change): self._source = new.getvalue() + def position_at_offset(self, offset): + lines = self.source[:offset].splitlines(True) + last_line = lines[-1] + if last_line.rstrip('\r\n') == last_line: + return {'line': len(lines) - 1, 'character': len(last_line)} + + return {'line': len(lines), 'character': 0} + def offset_at_position(self, position): """Return the byte-offset pointed at by the given position.""" return position['character'] + len(''.join(self.lines[:position['line']]))