Repository: palantir/python-language-server
Pull request #72 (2017-06-29 20:06:30)

Incremental file sync was broken for empty files


Patch:
diff --git a/pyls/workspace.py b/pyls/workspace.py
index b8381a9..3837b03 100644
--- a/pyls/workspace.py
+++ b/pyls/workspace.py
@@ -37,7 +37,6 @@ class Workspace(object):
 
     def put_document(self, doc_uri, content, version=None):
         path = urlparse(doc_uri).path
-        self._check_in_workspace(path)
         self._docs[doc_uri] = Document(
             doc_uri, content, sys_path=self.syspath_for_path(path), version=version
         )
@@ -72,9 +71,8 @@ class Workspace(object):
         path.extend(sys.path)
         return path
 
-    def _check_in_workspace(self, path):
-        if not os.path.commonprefix((self.root, path)):
-            raise ValueError("Document %s not in workspace %s" % (path, self.root))
+    def is_in_workspace(self, path):
+        return os.path.commonprefix((self.root, path))
 
 
 class Document(object):
@@ -94,11 +92,13 @@ class Document(object):
 
     @property
     def lines(self):
-        return self.source.splitlines(True)
+        # An empty document is much nicer to deal with assuming it has a single
+        # line with no characters and no final newline.
+        return self.source.splitlines(True) or [u'']
 
     @property
     def source(self):
-        if not self._source:
+        if self._source is None:
             with open(self.path, 'r') as f:
                 return f.read()
         return self._source


Test patch:
diff --git a/test/test_document.py b/test/test_document.py
index 9f47719..8e74a4a 100644
--- a/test/test_document.py
+++ b/test/test_document.py
@@ -36,3 +36,44 @@ 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'
+
+
+def test_document_empty_edit():
+    doc = Document('file:///uri', u'')
+    doc.apply_change({
+        'range': {
+            'start': {'line': 0, 'character': 0},
+            'end': {'line': 0, 'character': 0}
+        },
+        'text': u'f'
+    })
+    assert doc.source == u'f'
+
+
+def test_document_line_edit():
+    doc = Document('file:///uri', u'itshelloworld')
+    doc.apply_change({
+        'text': u'goodbye',
+        'range': {
+            'start': {'line': 0, 'character': 3},
+            'end': {'line': 0, 'character': 8}
+        }
+    })
+    assert doc.source == u'itsgoodbyeworld'
+
+
+def test_document_multiline_edit():
+    old = [
+        "def hello(a, b):\n",
+        "    print a\n",
+        "    print b\n"
+    ]
+    doc = Document('file:///uri', u''.join(old))
+    doc.apply_change({'text': u'print a, b', 'range': {
+        'start': {'line': 1, 'character': 4},
+        'end': {'line': 2, 'character': 11}
+    }})
+    assert doc.lines == [
+        "def hello(a, b):\n",
+        "    print a, b\n"
+    ]
diff --git a/test/test_workspace.py b/test/test_workspace.py
index fa262ae..f30afa7 100644
--- a/test/test_workspace.py
+++ b/test/test_workspace.py
@@ -51,32 +51,3 @@ def test_non_root_project(pyls):
     pyls.workspace.put_document(test_uri, 'assert True')
     test_doc = pyls.workspace.get_document(test_uri)
     assert project_root in pyls.workspace.syspath_for_path(test_doc.path)
-
-
-def test_document_line_edit():
-    doc = workspace.Document('file:///uri', u'itshelloworld')
-    doc.apply_change({
-        'text': u'goodbye',
-        'range': {
-            'start': {'line': 0, 'character': 3},
-            'end': {'line': 0, 'character': 8}
-        }
-    })
-    assert doc.source == u'itsgoodbyeworld'
-
-
-def test_document_multiline_edit():
-    old = [
-        "def hello(a, b):\n",
-        "    print a\n",
-        "    print b\n"
-    ]
-    doc = workspace.Document('file:///uri', u''.join(old))
-    doc.apply_change({'text': u'print a, b', 'range': {
-        'start': {'line': 1, 'character': 4},
-        'end': {'line': 2, 'character': 11}
-    }})
-    assert doc.lines == [
-        "def hello(a, b):\n",
-        "    print a, b\n"
-    ]