or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration-system.mdindex.mdplugin-development.mdserver-management.mdutilities-helpers.mdworkspace-management.md

plugin-development.mddocs/

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

```