Repository: palantir/python-language-server
Pull request #182 (2017-11-02 23:17:34)

Fixes for word_at_position
Hit when trying to run rope completions at the very end of a file.


Patch:
diff --git a/pyls/_utils.py b/pyls/_utils.py
index 894469d..34e8759 100644
--- a/pyls/_utils.py
+++ b/pyls/_utils.py
@@ -44,7 +44,11 @@ def race_hooks(hook_caller, pool, **kwargs):
         return None
 
     def _apply(impl):
-        return impl, impl.function(**kwargs)
+        try:
+            return impl, impl.function(**kwargs)
+        except Exception:
+            log.exception("Failed to run hook %s", impl.plugin_name)
+            raise
 
     # imap unordered gives us an iterator over the items in the order they finish.
     # We have to be careful to set chunksize to 1 to ensure hooks each get their own thread.
diff --git a/pyls/plugins/rope_completion.py b/pyls/plugins/rope_completion.py
index 7887185..299dd63 100644
--- a/pyls/plugins/rope_completion.py
+++ b/pyls/plugins/rope_completion.py
@@ -16,7 +16,7 @@ def pyls_completions(document, position):
     word = document.word_at_position({
         # The -1 should really be trying to look at the previous word, but that might be quite expensive
         # So we only skip import completions when the cursor is one space after `import`
-        'line': position['line'], 'character': position['character'] - 1,
+        'line': position['line'], 'character': max(position['character'] - 1, 0),
     })
     if word == 'import':
         return None
diff --git a/pyls/workspace.py b/pyls/workspace.py
index b548950..76dae4c 100644
--- a/pyls/workspace.py
+++ b/pyls/workspace.py
@@ -234,6 +234,9 @@ class Document(object):
 
     def word_at_position(self, position):
         """Get the word under the cursor returning the start and end positions."""
+        if position['line'] >= len(self.lines):
+            return ''
+
         line = self.lines[position['line']]
         i = position['character']
         # Split word in two


Test patch:
diff --git a/test/test_document.py b/test/test_document.py
index e8ef8bb..f53d4b2 100644
--- a/test/test_document.py
+++ b/test/test_document.py
@@ -1,5 +1,4 @@
 # Copyright 2017 Palantir Technologies, Inc.
-import os
 import pytest
 from pyls import uris
 from pyls.workspace import Document
@@ -32,6 +31,7 @@ def test_offset_at_position(doc):
     assert doc.offset_at_position({'line': 1, 'character': 5}) == 16
     assert doc.offset_at_position({'line': 2, 'character': 0}) == 12
     assert doc.offset_at_position({'line': 2, 'character': 4}) == 16
+    assert doc.offset_at_position({'line': 4, 'character': 0}) == 51
 
 
 def test_word_at_position(doc):
@@ -44,6 +44,8 @@ def test_word_at_position(doc):
     assert doc.word_at_position({'line': 1, 'character': 5}) == ''
     # def main():
     assert doc.word_at_position({'line': 2, 'character': 0}) == 'def'
+    # Past end of file
+    assert doc.word_at_position({'line': 4, 'character': 0}) == ''
 
 
 def test_document_empty_edit():
diff --git a/test/test_workspace.py b/test/test_workspace.py
index d7c462f..1d85ec1 100644
--- a/test/test_workspace.py
+++ b/test/test_workspace.py
@@ -1,7 +1,7 @@
 # Copyright 2017 Palantir Technologies, Inc.
 import os
 import pytest
-from pyls import uris, workspace
+from pyls import uris
 
 DOC_URI = uris.from_fs_path(__file__)