Skip to content

Commit 8315b59

Browse files
committed
Sendback diffs instead of the whole document with yapf formatting
1 parent 7e297f3 commit 8315b59

File tree

3 files changed

+64
-18
lines changed

3 files changed

+64
-18
lines changed

pylsp/plugins/yapf_format.py

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from yapf.yapflib import file_resources
77
from yapf.yapflib.yapf_api import FormatCode
88
from pylsp import hookimpl
9+
import whatthepatch
910

1011
log = logging.getLogger(__name__)
1112

@@ -34,10 +35,11 @@ def pylsp_format_range(document, range): # pylint: disable=redefined-builtin
3435

3536

3637
def _format(document, lines=None):
37-
new_source, changed = FormatCode(
38+
diff_txt, changed = FormatCode(
3839
document.source,
3940
lines=lines,
4041
filename=document.filename,
42+
print_diff=True,
4143
style_config=file_resources.GetDefaultStyleForDir(
4244
os.path.dirname(document.path)
4345
)
@@ -46,13 +48,57 @@ def _format(document, lines=None):
4648
if not changed:
4749
return []
4850

49-
# I'm too lazy at the moment to parse diffs into TextEdit items
50-
# So let's just return the entire file...
51-
return [{
52-
'range': {
53-
'start': {'line': 0, 'character': 0},
54-
# End char 0 of the line after our document
55-
'end': {'line': len(document.lines), 'character': 0}
56-
},
57-
'newText': new_source
58-
}]
51+
patch_generator = whatthepatch.parse_patch(diff_txt)
52+
diff = next(patch_generator)
53+
patch_generator.close()
54+
55+
# To keep things simple our text edits will be line based
56+
# and uncompacted
57+
textEdits = []
58+
# keep track of line number since additions
59+
# don't include the line number it's being added
60+
# to in diffs. lsp is 0-indexed so we'll start with -1
61+
prev_line_no = -1
62+
for change in diff.changes:
63+
if change.old and change.new:
64+
# no change
65+
# diffs are 1-indexed
66+
prev_line_no = change.old - 1
67+
elif change.new:
68+
# addition
69+
textEdits.append({
70+
'range': {
71+
'start': {
72+
'line': prev_line_no + 1,
73+
'character': 0
74+
},
75+
'end': {
76+
'line': prev_line_no + 1,
77+
'character': 0
78+
}
79+
},
80+
'newText': change.line + '\n'
81+
})
82+
elif change.old:
83+
# remove
84+
lsp_line_no = change.old - 1
85+
textEdits.append({
86+
'range': {
87+
'start': {
88+
'line': lsp_line_no,
89+
'character': 0
90+
},
91+
'end': {
92+
# From LSP spec:
93+
# If you want to specify a range that contains a line
94+
# including the line ending character(s) then use an
95+
# end position denoting the start of the next line.
96+
'line': lsp_line_no + 1,
97+
'character': 0
98+
}
99+
},
100+
'newText': ''
101+
})
102+
prev_line_no = lsp_line_no
103+
104+
return textEdits

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def get_version(module='pylsp'):
5555
'pylint>=2.5.0',
5656
'rope>=0.10.5',
5757
'yapf',
58+
'whatthepatch',
5859
],
5960
'autopep8': ['autopep8>=1.6.0,<1.7.0'],
6061
'flake8': ['flake8>=4.0.0,<4.1.0'],
@@ -64,7 +65,7 @@ def get_version(module='pylsp'):
6465
'pyflakes': ['pyflakes>=2.4.0,<2.5.0'],
6566
'pylint': ['pylint>=2.5.0'],
6667
'rope': ['rope>0.10.5'],
67-
'yapf': ['yapf'],
68+
'yapf': ['yapf', 'whatthepatch'],
6869
'test': ['pylint>=2.5.0', 'pytest', 'pytest-cov', 'coverage',
6970
'numpy', 'pandas', 'matplotlib', 'pyqt5', 'flaky'],
7071
},

test/plugins/test_yapf_format.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ def test_format(workspace):
2525
doc = Document(DOC_URI, workspace, DOC)
2626
res = pylsp_format_document(doc)
2727

28-
assert len(res) == 1
29-
assert res[0]['newText'] == "A = ['h', 'w', 'a']\n\nB = ['h', 'w']\n"
28+
assert doc.apply_text_edits(res) == "A = ['h', 'w', 'a']\n\nB = ['h', 'w']\n"
3029

3130

3231
def test_range_format(workspace):
@@ -38,10 +37,8 @@ def test_range_format(workspace):
3837
}
3938
res = pylsp_format_range(doc, def_range)
4039

41-
assert len(res) == 1
42-
4340
# Make sure B is still badly formatted
44-
assert res[0]['newText'] == "A = ['h', 'w', 'a']\n\nB = ['h',\n\n\n'w']\n"
41+
assert doc.apply_text_edits(res) == "A = ['h', 'w', 'a']\n\nB = ['h',\n\n\n'w']\n"
4542

4643

4744
def test_no_change(workspace):
@@ -56,5 +53,7 @@ def test_config_file(tmpdir, workspace):
5653
src = tmpdir.join('test.py')
5754
doc = Document(uris.from_fs_path(src.strpath), workspace, DOC)
5855

56+
res = pylsp_format_document(doc)
57+
5958
# A was split on multiple lines because of column_limit from config file
60-
assert pylsp_format_document(doc)[0]['newText'] == "A = [\n 'h', 'w',\n 'a'\n]\n\nB = ['h', 'w']\n"
59+
assert doc.apply_text_edits(res) == "A = [\n 'h', 'w',\n 'a'\n]\n\nB = ['h', 'w']\n"

0 commit comments

Comments
 (0)