Skip to content

Commit 4bffc39

Browse files
authored
Add support for LSP formatting options parameter (#134)
1 parent 52b684d commit 4bffc39

File tree

5 files changed

+85
-23
lines changed

5 files changed

+85
-23
lines changed

Diff for: pylsp/hookspecs.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ def pylsp_folding_range(config, workspace, document):
8080

8181

8282
@hookspec(firstresult=True)
83-
def pylsp_format_document(config, workspace, document):
83+
def pylsp_format_document(config, workspace, document, options):
8484
pass
8585

8686

8787
@hookspec(firstresult=True)
88-
def pylsp_format_range(config, workspace, document, range):
88+
def pylsp_format_range(config, workspace, document, range, options):
8989
pass
9090

9191

Diff for: pylsp/plugins/autopep8_format.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313

1414

1515
@hookimpl(tryfirst=True) # Prefer autopep8 over YAPF
16-
def pylsp_format_document(config, document):
16+
def pylsp_format_document(config, document, options=None): # pylint: disable=unused-argument
1717
log.info("Formatting document %s with autopep8", document)
1818
return _format(config, document)
1919

2020

2121
@hookimpl(tryfirst=True) # Prefer autopep8 over YAPF
22-
def pylsp_format_range(config, document, range): # pylint: disable=redefined-builtin
22+
def pylsp_format_range(config, document, range, options=None): # pylint: disable=redefined-builtin,unused-argument
2323
log.info("Formatting document %s in range %s with autopep8", document, range)
2424

2525
# First we 'round' the range up/down to full lines only

Diff for: pylsp/plugins/yapf_format.py

+46-9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import logging
55
import os
66

7-
from yapf.yapflib import file_resources
7+
from yapf.yapflib import file_resources, style
88
from yapf.yapflib.yapf_api import FormatCode
99

1010
from pylsp import hookimpl
@@ -14,12 +14,12 @@
1414

1515

1616
@hookimpl
17-
def pylsp_format_document(document):
18-
return _format(document)
17+
def pylsp_format_document(document, options=None):
18+
return _format(document, options=options)
1919

2020

2121
@hookimpl
22-
def pylsp_format_range(document, range): # pylint: disable=redefined-builtin
22+
def pylsp_format_range(document, range, options=None): # pylint: disable=redefined-builtin
2323
# First we 'round' the range up/down to full lines only
2424
range['start']['character'] = 0
2525
range['end']['line'] += 1
@@ -33,10 +33,10 @@ def pylsp_format_range(document, range): # pylint: disable=redefined-builtin
3333

3434
# Add 1 for 1-indexing vs LSP's 0-indexing
3535
lines = [(range['start']['line'] + 1, range['end']['line'] + 1)]
36-
return _format(document, lines=lines)
36+
return _format(document, lines=lines, options=options)
3737

3838

39-
def _format(document, lines=None):
39+
def _format(document, lines=None, options=None):
4040
# Yapf doesn't work with CRLF/CR line endings, so we replace them by '\n'
4141
# and restore them below.
4242
replace_eols = False
@@ -46,13 +46,50 @@ def _format(document, lines=None):
4646
replace_eols = True
4747
source = source.replace(eol_chars, '\n')
4848

49+
# Get the default styles as a string
50+
# for a preset configuration, i.e. "pep8"
51+
style_config = file_resources.GetDefaultStyleForDir(
52+
os.path.dirname(document.path)
53+
)
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)
60+
61+
use_tabs = style_config['USE_TABS']
62+
indent_width = style_config['INDENT_WIDTH']
63+
64+
if options.get('tabSize') is not None:
65+
indent_width = max(int(options.get('tabSize')), 1)
66+
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')
70+
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
75+
76+
style_config['USE_TABS'] = use_tabs
77+
style_config['INDENT_WIDTH'] = indent_width
78+
style_config['CONTINUATION_INDENT_WIDTH'] = indent_width
79+
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
85+
86+
style_config[style_option] = value
87+
4988
new_source, changed = FormatCode(
5089
source,
5190
lines=lines,
5291
filename=document.filename,
53-
style_config=file_resources.GetDefaultStyleForDir(
54-
os.path.dirname(document.path)
55-
)
92+
style_config=style_config
5693
)
5794

5895
if not changed:

Diff for: pylsp/python_lsp.py

+8-10
Original file line numberDiff line numberDiff line change
@@ -277,11 +277,11 @@ def document_symbols(self, doc_uri):
277277
def execute_command(self, command, arguments):
278278
return self._hook('pylsp_execute_command', command=command, arguments=arguments)
279279

280-
def format_document(self, doc_uri):
281-
return self._hook('pylsp_format_document', doc_uri)
280+
def format_document(self, doc_uri, options):
281+
return self._hook('pylsp_format_document', doc_uri, options=options)
282282

283-
def format_range(self, doc_uri, range):
284-
return self._hook('pylsp_format_range', doc_uri, range=range)
283+
def format_range(self, doc_uri, range, options):
284+
return self._hook('pylsp_format_range', doc_uri, range=range, options=options)
285285

286286
def highlight(self, doc_uri, position):
287287
return flatten(self._hook('pylsp_document_highlight', doc_uri, position=position)) or None
@@ -362,19 +362,17 @@ def m_text_document__hover(self, textDocument=None, position=None, **_kwargs):
362362
def m_text_document__document_symbol(self, textDocument=None, **_kwargs):
363363
return self.document_symbols(textDocument['uri'])
364364

365-
def m_text_document__formatting(self, textDocument=None, _options=None, **_kwargs):
366-
# For now we're ignoring formatting options.
367-
return self.format_document(textDocument['uri'])
365+
def m_text_document__formatting(self, textDocument=None, options=None, **_kwargs):
366+
return self.format_document(textDocument['uri'], options)
368367

369368
def m_text_document__rename(self, textDocument=None, position=None, newName=None, **_kwargs):
370369
return self.rename(textDocument['uri'], position, newName)
371370

372371
def m_text_document__folding_range(self, textDocument=None, **_kwargs):
373372
return self.folding(textDocument['uri'])
374373

375-
def m_text_document__range_formatting(self, textDocument=None, range=None, _options=None, **_kwargs):
376-
# Again, we'll ignore formatting options for now.
377-
return self.format_range(textDocument['uri'], range)
374+
def m_text_document__range_formatting(self, textDocument=None, range=None, options=None, **_kwargs):
375+
return self.format_range(textDocument['uri'], range, options)
378376

379377
def m_text_document__references(self, textDocument=None, position=None, context=None, **_kwargs):
380378
exclude_declaration = not context['includeDeclaration']

Diff for: test/plugins/test_yapf_format.py

+27
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
"""
2222

2323
GOOD_DOC = """A = ['hello', 'world']\n"""
24+
FOUR_SPACE_DOC = """def hello():
25+
pass
26+
"""
2427

2528

2629
def test_format(workspace):
@@ -68,3 +71,27 @@ def test_line_endings(workspace, newline):
6871
res = pylsp_format_document(doc)
6972

7073
assert res[0]['newText'] == f'import os{newline}import sys{2 * newline}dict(a=1){newline}'
74+
75+
76+
def test_format_with_tab_size_option(workspace):
77+
doc = Document(DOC_URI, workspace, FOUR_SPACE_DOC)
78+
res = pylsp_format_document(doc, {"tabSize": "8"})
79+
80+
assert len(res) == 1
81+
assert res[0]['newText'] == FOUR_SPACE_DOC.replace(" ", " ")
82+
83+
84+
def test_format_with_insert_spaces_option(workspace):
85+
doc = Document(DOC_URI, workspace, FOUR_SPACE_DOC)
86+
res = pylsp_format_document(doc, {"insertSpaces": False})
87+
88+
assert len(res) == 1
89+
assert res[0]['newText'] == FOUR_SPACE_DOC.replace(" ", "\t")
90+
91+
92+
def test_format_with_yapf_specific_option(workspace):
93+
doc = Document(DOC_URI, workspace, FOUR_SPACE_DOC)
94+
res = pylsp_format_document(doc, {"USE_TABS": True})
95+
96+
assert len(res) == 1
97+
assert res[0]['newText'] == FOUR_SPACE_DOC.replace(" ", "\t")

0 commit comments

Comments
 (0)