0
# Plugin Development
1
2
Comprehensive plugin system using Pluggy hooks for extending Python LSP Server functionality. Provides 40+ hook specifications covering all LSP features, document lifecycle, server management, and custom extensions.
3
4
## Capabilities
5
6
### Plugin Markers and Decorators
7
8
Core decorators for plugin development.
9
10
```python { .api }
11
hookimpl = pluggy.HookimplMarker("pylsp") # Mark hook implementations
12
hookspec = pluggy.HookspecMarker("pylsp") # Mark hook specifications
13
```
14
15
### Document Lifecycle Hooks
16
17
Hooks called during document open, save, and update operations.
18
19
```python { .api }
20
@hookimpl
21
def pylsp_document_did_open(config, workspace, document):
22
"""
23
Called when a document is opened.
24
25
Parameters:
26
- config: Config instance
27
- workspace: Workspace instance
28
- document: Document instance
29
"""
30
31
@hookimpl
32
def pylsp_document_did_save(config, workspace, document):
33
"""
34
Called when a document is saved.
35
36
Parameters:
37
- config: Config instance
38
- workspace: Workspace instance
39
- document: Document instance
40
"""
41
```
42
43
### Language Feature Hooks
44
45
Hooks for implementing core LSP language features.
46
47
```python { .api }
48
@hookimpl
49
def pylsp_completions(config, workspace, document, position, ignored_names):
50
"""
51
Provide code completions.
52
53
Parameters:
54
- config: Config instance
55
- workspace: Workspace instance
56
- document: Document instance
57
- position: dict with 'line' and 'character' keys
58
- ignored_names: list of names to ignore
59
60
Returns:
61
list: Completion items as dicts
62
"""
63
64
@hookimpl(hookwrapper=True) # firstresult hook
65
def pylsp_completion_item_resolve(config, workspace, document, completion_item):
66
"""
67
Resolve additional completion item information.
68
69
Parameters:
70
- config: Config instance
71
- workspace: Workspace instance
72
- document: Document instance
73
- completion_item: dict, completion item to resolve
74
75
Returns:
76
dict: Enhanced completion item
77
"""
78
79
@hookimpl
80
def pylsp_definitions(config, workspace, document, position):
81
"""
82
Provide go-to-definition locations.
83
84
Parameters:
85
- config: Config instance
86
- workspace: Workspace instance
87
- document: Document instance
88
- position: dict with 'line' and 'character' keys
89
90
Returns:
91
list: Location dicts with 'uri' and 'range'
92
"""
93
94
@hookimpl(hookwrapper=True) # firstresult hook
95
def pylsp_type_definition(config, document, position):
96
"""
97
Provide go-to-type-definition locations.
98
99
Parameters:
100
- config: Config instance
101
- document: Document instance
102
- position: dict with 'line' and 'character' keys
103
104
Returns:
105
list: Location dicts with 'uri' and 'range'
106
"""
107
108
@hookimpl(hookwrapper=True) # firstresult hook
109
def pylsp_hover(config, workspace, document, position):
110
"""
111
Provide hover information.
112
113
Parameters:
114
- config: Config instance
115
- workspace: Workspace instance
116
- document: Document instance
117
- position: dict with 'line' and 'character' keys
118
119
Returns:
120
dict: Hover response with 'contents' and optional 'range'
121
"""
122
123
@hookimpl(hookwrapper=True) # firstresult hook
124
def pylsp_signature_help(config, workspace, document, position):
125
"""
126
Provide signature help.
127
128
Parameters:
129
- config: Config instance
130
- workspace: Workspace instance
131
- document: Document instance
132
- position: dict with 'line' and 'character' keys
133
134
Returns:
135
dict: Signature help with 'signatures' list
136
"""
137
138
@hookimpl
139
def pylsp_document_symbols(config, workspace, document):
140
"""
141
Provide document symbols.
142
143
Parameters:
144
- config: Config instance
145
- workspace: Workspace instance
146
- document: Document instance
147
148
Returns:
149
list: Document symbol dicts
150
"""
151
152
@hookimpl
153
def pylsp_references(config, workspace, document, position, exclude_declaration):
154
"""
155
Find references to symbol.
156
157
Parameters:
158
- config: Config instance
159
- workspace: Workspace instance
160
- document: Document instance
161
- position: dict with 'line' and 'character' keys
162
- exclude_declaration: bool, exclude declaration from results
163
164
Returns:
165
list: Location dicts with 'uri' and 'range'
166
"""
167
168
@hookimpl(hookwrapper=True) # firstresult hook
169
def pylsp_rename(config, workspace, document, position, new_name):
170
"""
171
Rename symbol.
172
173
Parameters:
174
- config: Config instance
175
- workspace: Workspace instance
176
- document: Document instance
177
- position: dict with 'line' and 'character' keys
178
- new_name: str, new symbol name
179
180
Returns:
181
dict: WorkspaceEdit with document changes
182
"""
183
184
@hookimpl
185
def pylsp_document_highlight(config, workspace, document, position):
186
"""
187
Highlight occurrences of symbol.
188
189
Parameters:
190
- config: Config instance
191
- workspace: Workspace instance
192
- document: Document instance
193
- position: dict with 'line' and 'character' keys
194
195
Returns:
196
list: DocumentHighlight dicts with 'range' and 'kind'
197
"""
198
```
199
200
### Code Actions and Formatting Hooks
201
202
Hooks for code actions, formatting, and refactoring.
203
204
```python { .api }
205
@hookimpl
206
def pylsp_code_actions(config, workspace, document, range, context):
207
"""
208
Provide code actions.
209
210
Parameters:
211
- config: Config instance
212
- workspace: Workspace instance
213
- document: Document instance
214
- range: dict with 'start' and 'end' positions
215
- context: dict with diagnostics and action kinds
216
217
Returns:
218
list: CodeAction dicts
219
"""
220
221
@hookimpl
222
def pylsp_code_lens(config, workspace, document):
223
"""
224
Provide code lenses.
225
226
Parameters:
227
- config: Config instance
228
- workspace: Workspace instance
229
- document: Document instance
230
231
Returns:
232
list: CodeLens dicts with 'range' and 'command'
233
"""
234
235
@hookimpl(hookwrapper=True) # firstresult hook
236
def pylsp_format_document(config, workspace, document, options):
237
"""
238
Format entire document.
239
240
Parameters:
241
- config: Config instance
242
- workspace: Workspace instance
243
- document: Document instance
244
- options: dict with formatting options
245
246
Returns:
247
list: TextEdit dicts
248
"""
249
250
@hookimpl(hookwrapper=True) # firstresult hook
251
def pylsp_format_range(config, workspace, document, range, options):
252
"""
253
Format document range.
254
255
Parameters:
256
- config: Config instance
257
- workspace: Workspace instance
258
- document: Document instance
259
- range: dict with 'start' and 'end' positions
260
- options: dict with formatting options
261
262
Returns:
263
list: TextEdit dicts
264
"""
265
266
@hookimpl
267
def pylsp_folding_range(config, workspace, document):
268
"""
269
Provide folding ranges.
270
271
Parameters:
272
- config: Config instance
273
- workspace: Workspace instance
274
- document: Document instance
275
276
Returns:
277
list: FoldingRange dicts
278
"""
279
```
280
281
### Diagnostic and Linting Hooks
282
283
Hooks for providing diagnostics and linting information.
284
285
```python { .api }
286
@hookimpl
287
def pylsp_lint(config, workspace, document, is_saved):
288
"""
289
Provide linting diagnostics.
290
291
Parameters:
292
- config: Config instance
293
- workspace: Workspace instance
294
- document: Document instance
295
- is_saved: bool, whether document was just saved
296
297
Returns:
298
list: Diagnostic dicts with 'range', 'message', 'severity'
299
"""
300
```
301
302
### Server Lifecycle Hooks
303
304
Hooks called during server initialization, configuration changes, and shutdown.
305
306
```python { .api }
307
@hookimpl
308
def pylsp_initialize(config, workspace):
309
"""
310
Called during server initialization.
311
312
Parameters:
313
- config: Config instance
314
- workspace: Workspace instance
315
"""
316
317
@hookimpl
318
def pylsp_initialized():
319
"""Called after server initialization is complete."""
320
321
@hookimpl
322
def pylsp_shutdown(config, workspace):
323
"""
324
Called during server shutdown.
325
326
Parameters:
327
- config: Config instance
328
- workspace: Workspace instance
329
"""
330
331
@hookimpl
332
def pylsp_workspace_configuration_changed(config, workspace):
333
"""
334
Called when workspace configuration changes.
335
336
Parameters:
337
- config: Config instance
338
- workspace: Workspace instance
339
"""
340
```
341
342
### Extension and Command Hooks
343
344
Hooks for custom commands and experimental features.
345
346
```python { .api }
347
@hookimpl
348
def pylsp_commands(config, workspace):
349
"""
350
Provide custom command names.
351
352
Parameters:
353
- config: Config instance
354
- workspace: Workspace instance
355
356
Returns:
357
list: Command name strings
358
"""
359
360
@hookimpl(hookwrapper=True) # firstresult hook
361
def pylsp_execute_command(config, workspace, command, arguments):
362
"""
363
Execute custom command.
364
365
Parameters:
366
- config: Config instance
367
- workspace: Workspace instance
368
- command: str, command name
369
- arguments: list, command arguments
370
371
Returns:
372
any: Command result
373
"""
374
375
@hookimpl
376
def pylsp_dispatchers(config, workspace):
377
"""
378
Provide custom message dispatchers.
379
380
Parameters:
381
- config: Config instance
382
- workspace: Workspace instance
383
384
Returns:
385
dict: Method name to handler mappings
386
"""
387
388
@hookimpl
389
def pylsp_experimental_capabilities(config, workspace):
390
"""
391
Provide experimental server capabilities.
392
393
Parameters:
394
- config: Config instance
395
- workspace: Workspace instance
396
397
Returns:
398
dict: Experimental capabilities
399
"""
400
401
@hookimpl
402
def pylsp_settings(config):
403
"""
404
Provide plugin settings schema.
405
406
Parameters:
407
- config: Config instance
408
409
Returns:
410
dict: Settings schema
411
"""
412
```
413
414
## Built-in Plugins
415
416
Entry points for built-in plugins automatically loaded by the server.
417
418
```python { .api }
419
# From pyproject.toml [project.entry-points.pylsp]
420
BUILTIN_PLUGINS = {
421
"autopep8": "pylsp.plugins.autopep8_format",
422
"folding": "pylsp.plugins.folding",
423
"flake8": "pylsp.plugins.flake8_lint",
424
"jedi_completion": "pylsp.plugins.jedi_completion",
425
"jedi_definition": "pylsp.plugins.definition",
426
"jedi_type_definition": "pylsp.plugins.type_definition",
427
"jedi_hover": "pylsp.plugins.hover",
428
"jedi_highlight": "pylsp.plugins.highlight",
429
"jedi_references": "pylsp.plugins.references",
430
"jedi_rename": "pylsp.plugins.jedi_rename",
431
"jedi_signature_help": "pylsp.plugins.signature",
432
"jedi_symbols": "pylsp.plugins.symbols",
433
"mccabe": "pylsp.plugins.mccabe_lint",
434
"preload": "pylsp.plugins.preload_imports",
435
"pycodestyle": "pylsp.plugins.pycodestyle_lint",
436
"pydocstyle": "pylsp.plugins.pydocstyle_lint",
437
"pyflakes": "pylsp.plugins.pyflakes_lint",
438
"pylint": "pylsp.plugins.pylint_lint",
439
"rope_completion": "pylsp.plugins.rope_completion",
440
"rope_autoimport": "pylsp.plugins.rope_autoimport",
441
"yapf": "pylsp.plugins.yapf_format"
442
}
443
```
444
445
## Usage Examples
446
447
### Simple Linting Plugin
448
449
```python
450
from pylsp import hookimpl
451
452
@hookimpl
453
def pylsp_lint(config, workspace, document, is_saved):
454
"""Custom linter that flags TODO comments."""
455
diagnostics = []
456
lines = document.source.splitlines()
457
458
for line_num, line in enumerate(lines):
459
if 'TODO' in line:
460
todo_pos = line.find('TODO')
461
diagnostics.append({
462
"range": {
463
"start": {"line": line_num, "character": todo_pos},
464
"end": {"line": line_num, "character": todo_pos + 4}
465
},
466
"message": "TODO comment found",
467
"severity": 3, # Information
468
"source": "todo-linter"
469
})
470
471
return diagnostics
472
```
473
474
### Custom Completion Plugin
475
476
```python
477
from pylsp import hookimpl
478
479
@hookimpl
480
def pylsp_completions(config, workspace, document, position, ignored_names):
481
"""Provide custom completions for common patterns."""
482
completions = []
483
484
# Add custom completions based on context
485
word = document.word_at_position(position)
486
if word.startswith('log'):
487
completions.append({
488
"label": "logging.getLogger(__name__)",
489
"kind": 15, # Snippet
490
"detail": "Create logger instance",
491
"insertText": "logging.getLogger(__name__)",
492
"documentation": "Standard logger initialization pattern"
493
})
494
495
return completions
496
```
497
498
### Configuration-Driven Plugin
499
500
```python
501
from pylsp import hookimpl
502
503
@hookimpl
504
def pylsp_settings(config):
505
"""Define plugin settings schema."""
506
return {
507
"plugins": {
508
"my_plugin": {
509
"type": "object",
510
"properties": {
511
"enabled": {"type": "boolean", "default": True},
512
"severity": {"type": "string", "default": "warning"}
513
}
514
}
515
}
516
}
517
518
@hookimpl
519
def pylsp_lint(config, workspace, document, is_saved):
520
"""Use plugin configuration."""
521
settings = config.plugin_settings("my_plugin", document.path)
522
if not settings.get("enabled", True):
523
return []
524
525
severity_map = {"error": 1, "warning": 2, "info": 3, "hint": 4}
526
severity = severity_map.get(settings.get("severity", "warning"), 2)
527
528
# Perform linting with configured severity
529
return [{
530
"range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 1}},
531
"message": "Custom diagnostic",
532
"severity": severity,
533
"source": "my_plugin"
534
}]
535
```