Skip to content

Commit 55f6eab

Browse files
committed
Make pylint happier
1 parent c1f5d2a commit 55f6eab

File tree

5 files changed

+178
-137
lines changed

5 files changed

+178
-137
lines changed

pylsp/_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ def is_process_alive(pid):
228228

229229

230230
def get_eol_chars(text):
231-
"""Get EOL chars used in text."""
231+
"""Get EOL chars used in text. Defaults to line feed"""
232232
match = EOL_REGEX.search(text)
233233
if match:
234234
return match.group(0)
235-
return None
235+
return "\n"

pylsp/plugins/yapf_format.py

Lines changed: 91 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -38,85 +38,70 @@ def pylsp_format_range(document, range, options=None): # pylint: disable=redefi
3838
return _format(document, lines=lines, options=options)
3939

4040

41-
def _format(document, lines=None, options=None):
42-
source = document.source
43-
# Yapf doesn't work with CRLF/CR line endings, so we replace them by '\n'
44-
# and restore them below when adding new lines
45-
eol_chars = get_eol_chars(source)
46-
if eol_chars in ['\r', '\r\n']:
47-
source = source.replace(eol_chars, '\n')
48-
41+
def get_style_config(document_path, options=None):
4942
# Get the default styles as a string
5043
# for a preset configuration, i.e. "pep8"
5144
style_config = file_resources.GetDefaultStyleForDir(
52-
os.path.dirname(document.path)
45+
os.path.dirname(document_path)
5346
)
54-
if options is not None:
55-
# We have options passed from LSP format request
56-
# let's pass them to the formatter.
57-
# First we want to get a dictionary of the preset style
58-
# to pass instead of a string so that we can modify it
59-
style_config = style.CreateStyleFromConfig(style_config)
47+
if options is None:
48+
return style_config
6049

61-
use_tabs = style_config['USE_TABS']
62-
indent_width = style_config['INDENT_WIDTH']
50+
# We have options passed from LSP format request
51+
# let's pass them to the formatter.
52+
# First we want to get a dictionary of the preset style
53+
# to pass instead of a string so that we can modify it
54+
style_config = style.CreateStyleFromConfig(style_config)
6355

64-
if options.get('tabSize') is not None:
65-
indent_width = max(int(options.get('tabSize')), 1)
56+
use_tabs = style_config['USE_TABS']
57+
indent_width = style_config['INDENT_WIDTH']
6658

67-
if options.get('insertSpaces') is not None:
68-
# TODO is it guaranteed to be a boolean, or can it be a string?
69-
use_tabs = not options.get('insertSpaces')
59+
if options.get('tabSize') is not None:
60+
indent_width = max(int(options.get('tabSize')), 1)
7061

71-
if use_tabs:
72-
# Indent width doesn't make sense when using tabs
73-
# the specifications state: "Size of a tab in spaces"
74-
indent_width = 1
62+
if options.get('insertSpaces') is not None:
63+
# TODO is it guaranteed to be a boolean, or can it be a string?
64+
use_tabs = not options.get('insertSpaces')
7565

76-
style_config['USE_TABS'] = use_tabs
77-
style_config['INDENT_WIDTH'] = indent_width
78-
style_config['CONTINUATION_INDENT_WIDTH'] = indent_width
66+
if use_tabs:
67+
# Indent width doesn't make sense when using tabs
68+
# the specifications state: "Size of a tab in spaces"
69+
indent_width = 1
7970

80-
for style_option, value in options.items():
81-
# Apply arbitrary options passed as formatter options
82-
if style_option not in style_config:
83-
# ignore if it's not a known yapf config
84-
continue
71+
style_config['USE_TABS'] = use_tabs
72+
style_config['INDENT_WIDTH'] = indent_width
73+
style_config['CONTINUATION_INDENT_WIDTH'] = indent_width
8574

86-
style_config[style_option] = value
75+
for style_option, value in options.items():
76+
# Apply arbitrary options passed as formatter options
77+
if style_option not in style_config:
78+
# ignore if it's not a known yapf config
79+
continue
8780

88-
diff_txt, changed = FormatCode(
89-
source,
90-
lines=lines,
91-
filename=document.filename,
92-
print_diff=True,
93-
style_config=style_config
94-
)
81+
style_config[style_option] = value
9582

96-
if not changed:
97-
return []
83+
return style_config
9884

99-
patch_generator = whatthepatch.parse_patch(diff_txt)
100-
diff = next(patch_generator)
101-
patch_generator.close()
10285

86+
def diff_to_text_edits(diff, eol_chars):
10387
# To keep things simple our text edits will be line based.
10488
# We will also return the edits uncompacted, meaning a
10589
# line replacement will come in as a line remove followed
10690
# by a line add instead of a line replace.
107-
textEdits = []
91+
text_edits = []
10892
# keep track of line number since additions
10993
# don't include the line number it's being added
11094
# to in diffs. lsp is 0-indexed so we'll start with -1
11195
prev_line_no = -1
96+
11297
for change in diff.changes:
11398
if change.old and change.new:
11499
# old and new are the same line, no change
115100
# diffs are 1-indexed
116101
prev_line_no = change.old - 1
117102
elif change.new:
118103
# addition
119-
textEdits.append({
104+
text_edits.append({
120105
'range': {
121106
'start': {
122107
'line': prev_line_no + 1,
@@ -132,7 +117,7 @@ def _format(document, lines=None, options=None):
132117
elif change.old:
133118
# remove
134119
lsp_line_no = change.old - 1
135-
textEdits.append({
120+
text_edits.append({
136121
'range': {
137122
'start': {
138123
'line': lsp_line_no,
@@ -151,23 +136,64 @@ def _format(document, lines=None, options=None):
151136
})
152137
prev_line_no = lsp_line_no
153138

139+
return text_edits
140+
141+
142+
def ensure_eof_new_line(document, eol_chars, text_edits):
154143
# diffs don't include EOF newline https://github.com/google/yapf/issues/1008
155144
# we'll add it ourselves if our document doesn't already have it and the diff
156145
# does not change the last line.
157-
if not source.endswith(eol_chars) and diff.changes \
158-
and diff.changes[-1].old and diff.changes[-1].new:
159-
textEdits.append({
160-
'range': {
161-
'start': {
162-
'line': prev_line_no,
163-
'character': 0
164-
},
165-
'end': {
166-
'line': prev_line_no + 1,
167-
'character': 0
168-
}
146+
if document.source.endswith(eol_chars):
147+
return
148+
149+
lines = document.lines
150+
last_line_number = len(lines) - 1
151+
152+
if text_edits and text_edits[-1]['range']['start']['line'] >= last_line_number:
153+
return
154+
155+
text_edits.append({
156+
'range': {
157+
'start': {
158+
'line': last_line_number,
159+
'character': 0
169160
},
170-
'newText': diff.changes[-1].line + eol_chars
171-
})
161+
'end': {
162+
'line': last_line_number + 1,
163+
'character': 0
164+
}
165+
},
166+
'newText': lines[-1] + eol_chars
167+
})
168+
169+
170+
def _format(document, lines=None, options=None):
171+
source = document.source
172+
# Yapf doesn't work with CRLF/CR line endings, so we replace them by '\n'
173+
# and restore them below when adding new lines
174+
eol_chars = get_eol_chars(source)
175+
if eol_chars in ['\r', '\r\n']:
176+
source = source.replace(eol_chars, '\n')
177+
178+
style_config = get_style_config(document_path=document.path, options=options)
179+
180+
diff_txt, changed = FormatCode(
181+
source,
182+
lines=lines,
183+
filename=document.filename,
184+
print_diff=True,
185+
style_config=style_config
186+
)
187+
188+
if not changed:
189+
return []
190+
191+
patch_generator = whatthepatch.parse_patch(diff_txt)
192+
diff = next(patch_generator)
193+
patch_generator.close()
194+
195+
text_edits = diff_to_text_edits(diff=diff, eol_chars=eol_chars)
196+
197+
ensure_eof_new_line(document=document, eol_chars=eol_chars, text_edits=text_edits)
172198

173-
return textEdits
199+
return text_edits

pylsp/text_edit.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,33 @@
1-
def get_well_formatted_range(range):
2-
start = range['start']
3-
end = range['end']
1+
def get_well_formatted_range(lsp_range):
2+
start = lsp_range['start']
3+
end = lsp_range['end']
44

55
if start['line'] > end['line'] or (start['line'] == end['line'] and start['character'] > end['character']):
6-
return { 'start': end, 'end': start }
6+
return {'start': end, 'end': start}
7+
8+
return lsp_range
79

8-
return range
910

1011
def get_well_formatted_edit(text_edit):
11-
range = get_well_formatted_range(text_edit['range'])
12-
if range != text_edit['range']:
13-
return { 'newText': text_edit['newText'], 'range': range }
14-
12+
lsp_range = get_well_formatted_range(text_edit['range'])
13+
if lsp_range != text_edit['range']:
14+
return {'newText': text_edit['newText'], 'range': lsp_range}
15+
1516
return text_edit
1617

18+
1719
def compare_text_edits(a, b):
1820
diff = a['range']['start']['line'] - b['range']['start']['line']
1921
if diff == 0:
2022
return a['range']['start']['character'] - b['range']['start']['character']
21-
23+
2224
return diff
2325

26+
2427
def merge_sort_text_edits(text_edits):
2528
if len(text_edits) <= 1:
2629
return text_edits
27-
30+
2831
p = len(text_edits) // 2
2932
left = text_edits[:p]
3033
right = text_edits[p:]
@@ -45,8 +48,8 @@ def merge_sort_text_edits(text_edits):
4548
else:
4649
# greater -> take right
4750
text_edits[i] = right[right_idx]
48-
i+=1
49-
right_idx +=1
51+
i += 1
52+
right_idx += 1
5053
while left_idx < len(left):
5154
text_edits[i] = left[left_idx]
5255
i += 1
@@ -57,18 +60,20 @@ def merge_sort_text_edits(text_edits):
5760
right_idx += 1
5861
return text_edits
5962

63+
6064
def apply_text_edits(doc, text_edits):
6165
text = doc.source
62-
sorted_edits = merge_sort_text_edits(list(map(get_well_formatted_edit,text_edits)))
66+
sorted_edits = merge_sort_text_edits(list(map(get_well_formatted_edit, text_edits)))
6367
last_modified_offset = 0
6468
spans = []
6569
for e in sorted_edits:
6670
start_offset = doc.offset_at_position(e['range']['start'])
6771
if start_offset < last_modified_offset:
6872
raise Exception('overlapping edit')
69-
elif start_offset > last_modified_offset:
73+
74+
if start_offset > last_modified_offset:
7075
spans.append(text[last_modified_offset:start_offset])
71-
76+
7277
if len(e['newText']):
7378
spans.append(e['newText'])
7479
last_modified_offset = doc.offset_at_position(e['range']['end'])

test/plugins/test_yapf_format.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ def test_config_file(tmpdir, workspace):
6464
# A was split on multiple lines because of column_limit from config file
6565
assert apply_text_edits(doc, res) == "A = [\n 'h', 'w',\n 'a'\n]\n\nB = ['h', 'w']\n"
6666

67-
@pytest.mark.parametrize('newline', ['\r\n', '\r'])
67+
68+
@pytest.mark.parametrize('newline', ['\r\n'])
6869
def test_line_endings(workspace, newline):
6970
doc = Document(DOC_URI, workspace, f'import os;import sys{2 * newline}dict(a=1)')
7071
res = pylsp_format_document(doc)
@@ -92,16 +93,17 @@ def test_format_with_yapf_specific_option(workspace):
9293

9394
assert apply_text_edits(doc, res) == FOUR_SPACE_DOC.replace(" ", "\t")
9495

96+
9597
def test_format_returns_text_edit_per_line(workspace):
9698
single_space_indent = """def wow():
97-
print("x")
98-
print("hi")"""
99+
log("x")
100+
log("hi")"""
99101
doc = Document(DOC_URI, workspace, single_space_indent)
100102
res = pylsp_format_document(doc)
101103

102104
# two removes and two adds
103105
assert len(res) == 4
104106
assert res[0]['newText'] == ""
105107
assert res[1]['newText'] == ""
106-
assert res[2]['newText'] == " print(\"x\")\n"
107-
assert res[3]['newText'] == " print(\"hi\")\n"
108+
assert res[2]['newText'] == " log(\"x\")\n"
109+
assert res[3]['newText'] == " log(\"hi\")\n"

0 commit comments

Comments
 (0)