12
12
import sys
13
13
import sysconfig
14
14
import traceback
15
- from typing import Any , Dict , List , Optional , Sequence
15
+ from typing import Any , Dict , List , Optional , Sequence , Tuple
16
16
17
17
18
18
# **********************************************************
@@ -89,6 +89,12 @@ def update_environ_path() -> None:
89
89
# Minimum version of black supported.
90
90
MIN_VERSION = "22.3.0"
91
91
92
+ # Minimum version of black that supports the `--line-ranges` CLI option.
93
+ LINE_RANGES_MIN_VERSION = (23 , 11 , 0 )
94
+
95
+ # Versions of black found by workspace
96
+ VERSION_LOOKUP : Dict [str , Tuple [int , int , int ]] = {}
97
+
92
98
# **********************************************************
93
99
# Formatting features start here
94
100
# **********************************************************
@@ -102,13 +108,52 @@ def formatting(params: lsp.DocumentFormattingParams) -> list[lsp.TextEdit] | Non
102
108
return _formatting_helper (document )
103
109
104
110
105
- @LSP_SERVER .feature (lsp .TEXT_DOCUMENT_RANGE_FORMATTING )
106
- def range_formatting (params : lsp .DocumentFormattingParams ) -> list [lsp .TextEdit ] | None :
107
- """LSP handler for textDocument/formatting request."""
111
+ @LSP_SERVER .feature (
112
+ lsp .TEXT_DOCUMENT_RANGE_FORMATTING ,
113
+ lsp .DocumentRangeFormattingOptions (ranges_support = True ),
114
+ )
115
+ def range_formatting (
116
+ params : lsp .DocumentRangeFormattingParams ,
117
+ ) -> list [lsp .TextEdit ] | None :
118
+ """LSP handler for textDocument/rangeFormatting request."""
119
+ document = LSP_SERVER .workspace .get_text_document (params .text_document .uri )
120
+ settings = _get_settings_by_document (document )
121
+ version = VERSION_LOOKUP [settings ["workspaceFS" ]]
122
+
123
+ if version >= LINE_RANGES_MIN_VERSION :
124
+ return _formatting_helper (
125
+ document ,
126
+ args = [
127
+ "--line-ranges" ,
128
+ f"{ params .range .start .line + 1 } -{ params .range .end .line + 1 } " ,
129
+ ],
130
+ )
131
+ else :
132
+ log_warning (
133
+ "Black version earlier than 23.11.0 does not support range formatting. Formatting entire document."
134
+ )
135
+ return _formatting_helper (document )
136
+
108
137
109
- log_warning ("Black does not support range formatting. Formatting entire document." )
138
+ @LSP_SERVER .feature (lsp .TEXT_DOCUMENT_RANGES_FORMATTING )
139
+ def ranges_formatting (
140
+ params : lsp .DocumentRangesFormattingParams ,
141
+ ) -> list [lsp .TextEdit ] | None :
142
+ """LSP handler for textDocument/rangesFormatting request."""
110
143
document = LSP_SERVER .workspace .get_text_document (params .text_document .uri )
111
- return _formatting_helper (document )
144
+ settings = _get_settings_by_document (document )
145
+ version = VERSION_LOOKUP [settings ["workspaceFS" ]]
146
+
147
+ if version >= LINE_RANGES_MIN_VERSION :
148
+ args = []
149
+ for r in params .ranges :
150
+ args += ["--line-ranges" , f"{ r .start .line + 1 } -{ r .end .line + 1 } " ]
151
+ return _formatting_helper (document , args = args )
152
+ else :
153
+ log_warning (
154
+ "Black version earlier than 23.11.0 does not support range formatting. Formatting entire document."
155
+ )
156
+ return _formatting_helper (document )
112
157
113
158
114
159
def is_python (code : str , file_path : str ) -> bool :
@@ -121,8 +166,11 @@ def is_python(code: str, file_path: str) -> bool:
121
166
return True
122
167
123
168
124
- def _formatting_helper (document : workspace .Document ) -> list [lsp .TextEdit ] | None :
125
- extra_args = _get_args_by_file_extension (document )
169
+ def _formatting_helper (
170
+ document : workspace .Document , args : Sequence [str ] = None
171
+ ) -> list [lsp .TextEdit ] | None :
172
+ args = [] if args is None else args
173
+ extra_args = args + _get_args_by_file_extension (document )
126
174
extra_args += ["--stdin-filename" , _get_filename_for_black (document )]
127
175
result = _run_tool_on_document (document , use_stdin = True , extra_args = extra_args )
128
176
if result and result .stdout :
@@ -226,8 +274,6 @@ def initialize(params: lsp.InitializeParams) -> None:
226
274
paths = "\r \n " .join (sys .path )
227
275
log_to_output (f"sys.path used to run Server:\r \n { paths } " )
228
276
229
- _log_version_info ()
230
-
231
277
232
278
@LSP_SERVER .feature (lsp .EXIT )
233
279
def on_exit (_params : Optional [Any ] = None ) -> None :
@@ -241,12 +287,13 @@ def on_shutdown(_params: Optional[Any] = None) -> None:
241
287
jsonrpc .shutdown_json_rpc ()
242
288
243
289
244
- def _log_version_info () -> None :
245
- for value in WORKSPACE_SETTINGS .values ():
290
+ def _update_workspace_settings_with_version_info (
291
+ workspace_settings : dict [str , Any ]
292
+ ) -> None :
293
+ for settings in workspace_settings .values ():
246
294
try :
247
295
from packaging .version import parse as parse_version
248
296
249
- settings = copy .deepcopy (value )
250
297
result = _run_tool (["--version" ], settings )
251
298
code_workspace = settings ["workspaceFS" ]
252
299
log_to_output (
@@ -269,6 +316,11 @@ def _log_version_info() -> None:
269
316
270
317
version = parse_version (actual_version )
271
318
min_version = parse_version (MIN_VERSION )
319
+ VERSION_LOOKUP [code_workspace ] = (
320
+ version .major ,
321
+ version .minor ,
322
+ version .micro ,
323
+ )
272
324
273
325
if version < min_version :
274
326
log_error (
@@ -281,6 +333,7 @@ def _log_version_info() -> None:
281
333
f"SUPPORTED { TOOL_MODULE } >={ min_version } \r \n "
282
334
f"FOUND { TOOL_MODULE } =={ actual_version } \r \n "
283
335
)
336
+
284
337
except : # pylint: disable=bare-except
285
338
log_to_output (
286
339
f"Error while detecting black version:\r \n { traceback .format_exc ()} "
@@ -318,6 +371,8 @@ def _update_workspace_settings(settings):
318
371
"workspaceFS" : key ,
319
372
}
320
373
374
+ _update_workspace_settings_with_version_info (WORKSPACE_SETTINGS )
375
+
321
376
322
377
def _get_settings_by_path (file_path : pathlib .Path ):
323
378
workspaces = {s ["workspaceFS" ] for s in WORKSPACE_SETTINGS .values ()}
0 commit comments