0
# Code Formatters
1
2
Extensible code formatter system supporting multiple programming languages and formatter tools with configurable options, magic command handling, and error management.
3
4
## Capabilities
5
6
### Base Formatter Class
7
8
Abstract base class defining the formatter interface.
9
10
```python { .api }
11
class BaseFormatter(abc.ABC):
12
@property
13
@abc.abstractmethod
14
def label(self) -> str:
15
"""Human-readable formatter name"""
16
pass
17
18
@property
19
@abc.abstractmethod
20
def importable(self) -> bool:
21
"""Whether formatter dependencies are available"""
22
pass
23
24
@abc.abstractmethod
25
def format_code(self, code: str, notebook: bool, **options) -> str:
26
"""Format code with specified options"""
27
pass
28
29
@property
30
def cached_importable(self) -> bool:
31
"""Cached version of importable check"""
32
return self.importable
33
```
34
35
### Python Formatters
36
37
#### Black Formatter
38
39
Python code formatter using Black.
40
41
```python { .api }
42
class BlackFormatter(BaseFormatter):
43
label = "Apply Black Formatter"
44
45
@property
46
def importable(self) -> bool:
47
"""Check if black is available"""
48
pass
49
50
def format_code(self, code: str, notebook: bool, **options) -> str:
51
"""Format code using Black"""
52
pass
53
```
54
55
**Supported Options**:
56
- `line_length` (int) - Maximum line length
57
- `string_normalization` (bool) - Normalize string quotes
58
- `magic_trailing_comma` (bool) - Use trailing commas
59
- `experimental_string_processing` (bool) - Enable experimental features
60
- `preview` (bool) - Enable preview features
61
62
#### Blue Formatter
63
64
Python code formatter using Blue (Black fork).
65
66
```python { .api }
67
class BlueFormatter(BaseFormatter):
68
label = "Apply Blue Formatter"
69
70
@property
71
def importable(self) -> bool:
72
"""Check if blue is available"""
73
pass
74
75
def format_code(self, code: str, notebook: bool, **options) -> str:
76
"""Format code using Blue"""
77
pass
78
```
79
80
#### Autopep8 Formatter
81
82
Python code formatter using autopep8.
83
84
```python { .api }
85
class Autopep8Formatter(BaseFormatter):
86
label = "Apply Autopep8 Formatter"
87
88
@property
89
def importable(self) -> bool:
90
"""Check if autopep8 is available"""
91
pass
92
93
def format_code(self, code: str, notebook: bool, **options) -> str:
94
"""Format code using autopep8"""
95
pass
96
```
97
98
#### YAPF Formatter
99
100
Python code formatter using YAPF.
101
102
```python { .api }
103
class YapfFormatter(BaseFormatter):
104
label = "Apply YAPF Formatter"
105
106
@property
107
def importable(self) -> bool:
108
"""Check if yapf is available"""
109
pass
110
111
def format_code(self, code: str, notebook: bool, **options) -> str:
112
"""Format code using YAPF"""
113
pass
114
```
115
116
#### Isort Formatter
117
118
Python import sorter using isort.
119
120
```python { .api }
121
class IsortFormatter(BaseFormatter):
122
label = "Apply Isort Formatter"
123
124
@property
125
def importable(self) -> bool:
126
"""Check if isort is available"""
127
pass
128
129
def format_code(self, code: str, notebook: bool, **options) -> str:
130
"""Sort imports using isort"""
131
pass
132
```
133
134
#### Ruff Formatters
135
136
Python linter/formatter using Ruff.
137
138
```python { .api }
139
class RuffFixFormatter(CommandLineFormatter):
140
label = "Apply ruff fix"
141
142
def __init__(self):
143
"""Initialize with ruff fix command"""
144
pass
145
146
class RuffFormatFormatter(RuffFixFormatter):
147
label = "Apply ruff formatter"
148
149
def __init__(self):
150
"""Initialize with ruff format command"""
151
pass
152
```
153
154
### R Formatters
155
156
#### FormatR Formatter
157
158
R code formatter using formatR package.
159
160
```python { .api }
161
class FormatRFormatter(RFormatter):
162
label = "Apply FormatR Formatter"
163
package_name = "formatR"
164
165
@property
166
def importable(self) -> bool:
167
"""Check if formatR package is available"""
168
pass
169
170
def format_code(self, code: str, notebook: bool, **options) -> str:
171
"""Format R code using formatR"""
172
pass
173
```
174
175
#### Styler Formatter
176
177
R code formatter using styler package.
178
179
```python { .api }
180
class StylerFormatter(RFormatter):
181
label = "Apply Styler Formatter"
182
package_name = "styler"
183
184
@property
185
def importable(self) -> bool:
186
"""Check if styler package is available"""
187
pass
188
189
def format_code(self, code: str, notebook: bool, **options) -> str:
190
"""Format R code using styler"""
191
pass
192
```
193
194
### Generic Command Line Formatter
195
196
Wrapper for command-line formatters.
197
198
```python { .api }
199
class CommandLineFormatter(BaseFormatter):
200
command: List[str]
201
202
def __init__(self, command: List[str]):
203
"""Initialize with command list"""
204
self.command = command
205
206
@property
207
def label(self) -> str:
208
"""Generate label from command name"""
209
return f"Apply {self.command[0]} Formatter"
210
211
@property
212
def importable(self) -> bool:
213
"""Check if command exists"""
214
pass
215
216
def format_code(self, code: str, notebook: bool, args: List[str] = [], **options) -> str:
217
"""Format code using command line tool"""
218
pass
219
```
220
221
## Magic Command Handling
222
223
### Base Line Escaper
224
225
Abstract base class for handling special syntax.
226
227
```python { .api }
228
class BaseLineEscaper(abc.ABC):
229
def __init__(self, code: str) -> None:
230
self.code = code
231
232
@property
233
@abc.abstractmethod
234
def langs(self) -> List[str]:
235
"""Supported programming languages"""
236
pass
237
238
@abc.abstractmethod
239
def escape(self, line: str) -> str:
240
"""Escape special syntax before formatting"""
241
pass
242
243
@abc.abstractmethod
244
def unescape(self, line: str) -> str:
245
"""Restore special syntax after formatting"""
246
pass
247
```
248
249
### Specific Escapers
250
251
```python { .api }
252
class MagicCommandEscaper(BaseLineEscaper):
253
"""Handles Jupyter magic commands (% and %%)"""
254
langs = ["python"]
255
256
class HelpEscaper(BaseLineEscaper):
257
"""Handles help commands (? and ??)"""
258
langs = ["python"]
259
260
class CommandEscaper(BaseLineEscaper):
261
"""Handles shell commands (!)"""
262
langs = ["python"]
263
264
class QuartoCommentEscaper(BaseLineEscaper):
265
"""Handles Quarto comments (#|)"""
266
langs = ["python"]
267
268
class RunScriptEscaper(BaseLineEscaper):
269
"""Handles run script commands"""
270
langs = ["python"]
271
```
272
273
## Formatter Registry
274
275
### Server Formatters Dictionary
276
277
Global registry of all available formatters.
278
279
```python { .api }
280
SERVER_FORMATTERS: Dict[str, BaseFormatter] = {
281
"black": BlackFormatter(),
282
"blue": BlueFormatter(),
283
"autopep8": Autopep8Formatter(),
284
"yapf": YapfFormatter(),
285
"isort": IsortFormatter(),
286
"ruff": RuffFixFormatter(),
287
"ruffformat": RuffFormatFormatter(),
288
"formatR": FormatRFormatter(),
289
"styler": StylerFormatter(),
290
"scalafmt": CommandLineFormatter(command=["scalafmt", "--stdin"]),
291
"rustfmt": CommandLineFormatter(command=["rustfmt"]),
292
"astyle": CommandLineFormatter(command=["astyle"]),
293
}
294
```
295
296
### Escaper Classes Registry
297
298
List of all available line escaper classes.
299
300
```python { .api }
301
ESCAPER_CLASSES: List[Type[BaseLineEscaper]] = [
302
MagicCommandEscaper,
303
HelpEscaper,
304
CommandEscaper,
305
QuartoCommentEscaper,
306
RunScriptEscaper,
307
]
308
```
309
310
## Usage Examples
311
312
### Using Python Formatters
313
314
```python
315
from jupyterlab_code_formatter.formatters import BlackFormatter, IsortFormatter
316
317
# Create formatter instances
318
black_formatter = BlackFormatter()
319
isort_formatter = IsortFormatter()
320
321
# Check availability
322
if black_formatter.importable:
323
# Format code with options
324
formatted_code = black_formatter.format_code(
325
code="def hello( ):\n pass",
326
notebook=True,
327
line_length=88,
328
string_normalization=True
329
)
330
print(formatted_code) # "def hello():\n pass"
331
332
# Format imports
333
if isort_formatter.importable:
334
formatted_imports = isort_formatter.format_code(
335
code="import os\nimport sys\nimport numpy",
336
notebook=False,
337
multi_line_output=3,
338
include_trailing_comma=True
339
)
340
```
341
342
### Using Command Line Formatters
343
344
```python
345
from jupyterlab_code_formatter.formatters import CommandLineFormatter
346
347
# Create Rust formatter
348
rustfmt = CommandLineFormatter(command=["rustfmt"])
349
350
# Check if rustfmt command exists
351
if rustfmt.importable:
352
formatted_rust = rustfmt.format_code(
353
code="fn main(){println!(\"Hello\");}",
354
notebook=False,
355
args=["--edition", "2021"]
356
)
357
```
358
359
### Using R Formatters
360
361
```python
362
from jupyterlab_code_formatter.formatters import StylerFormatter
363
364
# Create styler formatter
365
styler = StylerFormatter()
366
367
# Check if R and styler package are available
368
if styler.importable:
369
formatted_r = styler.format_code(
370
code="x<-c(1,2,3)\ny<-mean(x)",
371
notebook=True,
372
scope="tokens",
373
indent_by=2
374
)
375
```
376
377
### Custom Formatter Implementation
378
379
```python
380
from jupyterlab_code_formatter.formatters import BaseFormatter
381
import subprocess
382
383
class CustomFormatter(BaseFormatter):
384
@property
385
def label(self) -> str:
386
return "Apply Custom Formatter"
387
388
@property
389
def importable(self) -> bool:
390
# Check if custom formatter is available
391
return shutil.which("custom-formatter") is not None
392
393
def format_code(self, code: str, notebook: bool, **options) -> str:
394
# Custom formatting logic
395
process = subprocess.run(
396
["custom-formatter", "--stdin"],
397
input=code,
398
stdout=subprocess.PIPE,
399
stderr=subprocess.PIPE,
400
universal_newlines=True
401
)
402
403
if process.stderr:
404
raise Exception(f"Formatter error: {process.stderr}")
405
406
return process.stdout
407
408
# Register custom formatter
409
from jupyterlab_code_formatter.formatters import SERVER_FORMATTERS
410
SERVER_FORMATTERS["custom"] = CustomFormatter()
411
```
412
413
## Utility Functions
414
415
### Formatter Availability
416
417
```python { .api }
418
def is_importable(pkg_name: str) -> bool:
419
"""Check if Python package is importable"""
420
pass
421
422
def command_exist(name: str) -> bool:
423
"""Check if command-line tool exists"""
424
pass
425
```
426
427
### Black/Blue Compatibility
428
429
```python { .api }
430
def import_black():
431
"""Import black with Blue compatibility handling"""
432
pass
433
434
def import_blue():
435
"""Import blue and perform monkey patching"""
436
pass
437
```
438
439
### Line Ending and Magic Decorator
440
441
```python { .api }
442
def handle_line_ending_and_magic(func):
443
"""Decorator for handling magic commands and line endings"""
444
pass
445
```
446
447
## Configuration Examples
448
449
### Formatter-Specific Options
450
451
```python
452
# Black formatter options
453
black_options = {
454
"line_length": 88,
455
"string_normalization": True,
456
"magic_trailing_comma": True,
457
"experimental_string_processing": False,
458
"preview": False
459
}
460
461
# YAPF formatter options
462
yapf_options = {
463
"based_on_style": "pep8",
464
"column_limit": 88,
465
"dedent_closing_brackets": True,
466
"indent_width": 4
467
}
468
469
# Isort formatter options
470
isort_options = {
471
"multi_line_output": 3,
472
"include_trailing_comma": True,
473
"force_grid_wrap": 0,
474
"use_parentheses": True,
475
"line_length": 88
476
}
477
```
478
479
### Language Support
480
481
**Supported Languages and Formatters**:
482
- **Python**: black, blue, autopep8, yapf, isort, ruff
483
- **R**: formatR, styler
484
- **Scala**: scalafmt
485
- **Rust**: rustfmt
486
- **C/C++**: astyle
487
488
**Extensibility**: New formatters can be added by implementing `BaseFormatter` or using `CommandLineFormatter` for command-line tools.