or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-operations.mdexception-handling.mdfeature-registration.mdindex.mdprogress-reporting.mdprotocol-handling.mdserver-management.mduri-utilities.mdutilities.mdworkspace-management.md

progress-reporting.mddocs/

0

# Progress Reporting

1

2

Built-in progress reporting system for long-running operations with client-side progress bar integration, cancellation support, and comprehensive progress lifecycle management.

3

4

## Capabilities

5

6

### Progress Class

7

8

Central progress management system that handles work-done progress reporting with client integration and cancellation support.

9

10

```python { .api }

11

class Progress:

12

"""

13

Progress reporting system for long-running operations.

14

15

Manages client-side progress bars, handles cancellation requests,

16

and provides comprehensive progress lifecycle management for

17

language server operations.

18

"""

19

20

def __init__(self, lsp: LanguageServerProtocol) -> None:

21

"""

22

Initialize progress manager.

23

24

Parameters:

25

- lsp: LanguageServerProtocol - Protocol instance for client communication

26

"""

27

28

def create_task(

29

self,

30

token: ProgressToken,

31

title: str,

32

cancellable: bool = True,

33

message: str = None,

34

percentage: int = None

35

) -> Future:

36

"""

37

Create progress task with client progress bar.

38

39

Parameters:

40

- token: ProgressToken - Unique token for progress tracking

41

- title: str - Progress task title shown to user

42

- cancellable: bool - Whether task can be cancelled (default: True)

43

- message: str - Optional initial message

44

- percentage: int - Optional initial percentage (0-100)

45

46

Returns:

47

Future that resolves when progress is created or fails

48

"""

49

50

def update(

51

self,

52

token: ProgressToken,

53

message: str = None,

54

percentage: int = None

55

) -> None:

56

"""

57

Update progress with new message or percentage.

58

59

Parameters:

60

- token: ProgressToken - Progress token to update

61

- message: str - Optional progress message

62

- percentage: int - Optional progress percentage (0-100)

63

"""

64

65

def end(self, token: ProgressToken, message: str = None) -> None:

66

"""

67

End progress reporting.

68

69

Parameters:

70

- token: ProgressToken - Progress token to end

71

- message: str - Optional final message

72

"""

73

74

@property

75

def tokens(self) -> Dict[ProgressToken, Future]:

76

"""Access to active progress tokens and their futures."""

77

```

78

79

### Progress Message Types

80

81

Core progress message types for LSP work-done progress protocol implementation.

82

83

```python { .api }

84

# Progress message types from lsprotocol.types

85

86

class WorkDoneProgressBegin:

87

"""

88

Progress begin notification structure.

89

90

Attributes:

91

- title: str - Progress title

92

- cancellable: bool - Whether progress can be cancelled

93

- message: str - Optional message

94

- percentage: int - Optional percentage

95

"""

96

97

class WorkDoneProgressReport:

98

"""

99

Progress update notification structure.

100

101

Attributes:

102

- message: str - Optional progress message

103

- percentage: int - Optional percentage (0-100)

104

- cancellable: bool - Whether progress can be cancelled

105

"""

106

107

class WorkDoneProgressEnd:

108

"""

109

Progress end notification structure.

110

111

Attributes:

112

- message: str - Optional final message

113

"""

114

115

class WorkDoneProgressCreateParams:

116

"""

117

Parameters for creating work-done progress.

118

119

Attributes:

120

- token: ProgressToken - Progress token

121

"""

122

123

class ProgressParams:

124

"""

125

Progress notification parameters.

126

127

Attributes:

128

- token: ProgressToken - Progress token

129

- value: Union[WorkDoneProgressBegin, WorkDoneProgressReport, WorkDoneProgressEnd]

130

"""

131

```

132

133

## Usage Examples

134

135

### Basic Progress Reporting

136

137

```python

138

import asyncio

139

import uuid

140

from pygls.server import LanguageServer

141

from pygls.progress import Progress

142

143

server = LanguageServer("progress-example", "1.0.0")

144

145

@server.command("myServer.longOperation")

146

async def long_operation(params):

147

# Create unique progress token

148

token = str(uuid.uuid4())

149

150

# Start progress

151

await server.lsp.progress.create_task(

152

token=token,

153

title="Processing Files",

154

cancellable=True,

155

message="Starting analysis..."

156

)

157

158

try:

159

# Simulate long-running work with progress updates

160

total_files = 100

161

162

for i in range(total_files):

163

# Check if operation was cancelled

164

if token in server.lsp.progress.tokens:

165

future = server.lsp.progress.tokens[token]

166

if future.cancelled():

167

return {"cancelled": True}

168

169

# Simulate file processing

170

await asyncio.sleep(0.1)

171

172

# Update progress

173

percentage = int((i + 1) / total_files * 100)

174

server.lsp.progress.update(

175

token=token,

176

message=f"Processing file {i + 1}/{total_files}",

177

percentage=percentage

178

)

179

180

# End progress

181

server.lsp.progress.end(

182

token=token,

183

message="Analysis completed successfully"

184

)

185

186

return {"result": "Operation completed", "files_processed": total_files}

187

188

except Exception as e:

189

# End progress with error

190

server.lsp.progress.end(

191

token=token,

192

message=f"Operation failed: {str(e)}"

193

)

194

raise

195

```

196

197

### Progress with Cancellation Handling

198

199

```python

200

import time

201

from concurrent.futures import ThreadPoolExecutor

202

203

@server.command("myServer.heavyComputation")

204

@server.thread()

205

def heavy_computation(params):

206

"""Long-running computation with cancellation support."""

207

token = str(uuid.uuid4())

208

209

# Start progress on main thread

210

asyncio.run_coroutine_threadsafe(

211

server.lsp.progress.create_task(

212

token=token,

213

title="Heavy Computation",

214

cancellable=True,

215

message="Initializing computation..."

216

),

217

server.loop

218

)

219

220

try:

221

total_iterations = 1000

222

223

for i in range(total_iterations):

224

# Check cancellation status

225

if token in server.lsp.progress.tokens:

226

future = server.lsp.progress.tokens[token]

227

if future.cancelled():

228

return {"cancelled": True, "iterations_completed": i}

229

230

# Simulate heavy computation

231

time.sleep(0.01) # Blocking operation

232

233

# Update progress every 10 iterations

234

if i % 10 == 0:

235

percentage = int(i / total_iterations * 100)

236

237

# Update from thread

238

def update_progress():

239

server.lsp.progress.update(

240

token=token,

241

message=f"Computing iteration {i}/{total_iterations}",

242

percentage=percentage

243

)

244

245

asyncio.run_coroutine_threadsafe(

246

asyncio.create_task(update_progress()),

247

server.loop

248

)

249

250

# Complete progress

251

def end_progress():

252

server.lsp.progress.end(

253

token=token,

254

message="Computation completed"

255

)

256

257

asyncio.run_coroutine_threadsafe(

258

asyncio.create_task(end_progress()),

259

server.loop

260

)

261

262

return {"result": "Computation completed", "iterations": total_iterations}

263

264

except Exception as e:

265

# Handle error in progress

266

def end_with_error():

267

server.lsp.progress.end(

268

token=token,

269

message=f"Computation failed: {str(e)}"

270

)

271

272

asyncio.run_coroutine_threadsafe(

273

asyncio.create_task(end_with_error()),

274

server.loop

275

)

276

raise

277

```

278

279

### Multiple Progress Tasks

280

281

```python

282

@server.command("myServer.multiStepProcess")

283

async def multi_step_process(params):

284

"""Process with multiple progress bars for different steps."""

285

286

# Step 1: File scanning

287

scan_token = str(uuid.uuid4())

288

await server.lsp.progress.create_task(

289

token=scan_token,

290

title="Scanning Files",

291

cancellable=False,

292

message="Scanning project directory..."

293

)

294

295

files = []

296

for i in range(50):

297

await asyncio.sleep(0.02)

298

files.append(f"file_{i}.py")

299

300

server.lsp.progress.update(

301

token=scan_token,

302

message=f"Found {len(files)} files",

303

percentage=int(i / 50 * 100)

304

)

305

306

server.lsp.progress.end(scan_token, "File scanning completed")

307

308

# Step 2: Analysis

309

analysis_token = str(uuid.uuid4())

310

await server.lsp.progress.create_task(

311

token=analysis_token,

312

title="Analyzing Files",

313

cancellable=True,

314

message="Starting analysis..."

315

)

316

317

analysis_results = {}

318

for i, file in enumerate(files):

319

# Check cancellation

320

if analysis_token in server.lsp.progress.tokens:

321

future = server.lsp.progress.tokens[analysis_token]

322

if future.cancelled():

323

return {"cancelled": True, "partial_results": analysis_results}

324

325

await asyncio.sleep(0.05)

326

analysis_results[file] = {"lines": i * 10, "functions": i * 2}

327

328

percentage = int((i + 1) / len(files) * 100)

329

server.lsp.progress.update(

330

token=analysis_token,

331

message=f"Analyzed {i + 1}/{len(files)} files",

332

percentage=percentage

333

)

334

335

server.lsp.progress.end(analysis_token, "Analysis completed")

336

337

return {

338

"files_scanned": len(files),

339

"analysis_results": analysis_results

340

}

341

```

342

343

### Progress Context Manager

344

345

```python

346

from contextlib import asynccontextmanager

347

348

class ProgressManager:

349

def __init__(self, server):

350

self.server = server

351

352

@asynccontextmanager

353

async def progress_context(

354

self,

355

title: str,

356

cancellable: bool = True,

357

initial_message: str = None

358

):

359

"""Context manager for automatic progress lifecycle management."""

360

token = str(uuid.uuid4())

361

362

try:

363

# Start progress

364

await self.server.lsp.progress.create_task(

365

token=token,

366

title=title,

367

cancellable=cancellable,

368

message=initial_message or "Starting..."

369

)

370

371

# Yield progress controller

372

controller = ProgressController(self.server, token)

373

yield controller

374

375

finally:

376

# Always end progress

377

self.server.lsp.progress.end(

378

token=token,

379

message="Operation completed"

380

)

381

382

class ProgressController:

383

def __init__(self, server, token):

384

self.server = server

385

self.token = token

386

387

def update(self, message: str = None, percentage: int = None):

388

"""Update progress."""

389

self.server.lsp.progress.update(

390

token=self.token,

391

message=message,

392

percentage=percentage

393

)

394

395

def is_cancelled(self) -> bool:

396

"""Check if progress was cancelled."""

397

if self.token in self.server.lsp.progress.tokens:

398

future = self.server.lsp.progress.tokens[self.token]

399

return future.cancelled()

400

return False

401

402

# Usage with context manager

403

progress_manager = ProgressManager(server)

404

405

@server.command("myServer.contextManagedOperation")

406

async def context_managed_operation(params):

407

async with progress_manager.progress_context(

408

title="Context Managed Operation",

409

cancellable=True,

410

initial_message="Initializing..."

411

) as progress:

412

413

for i in range(100):

414

if progress.is_cancelled():

415

return {"cancelled": True}

416

417

await asyncio.sleep(0.01)

418

419

progress.update(

420

message=f"Step {i + 1}/100",

421

percentage=i + 1

422

)

423

424

return {"result": "Operation completed successfully"}

425

```

426

427

### Custom Progress Notifications

428

429

```python

430

from lsprotocol.types import (

431

PROGRESS,

432

WINDOW_WORK_DONE_PROGRESS_CREATE,

433

WorkDoneProgressBegin,

434

WorkDoneProgressReport,

435

WorkDoneProgressEnd

436

)

437

438

class CustomProgress:

439

def __init__(self, server):

440

self.server = server

441

self.active_progress = {}

442

443

async def start_custom_progress(

444

self,

445

token: str,

446

title: str,

447

custom_data: dict = None

448

):

449

"""Start progress with custom data."""

450

451

# Create progress

452

await self.server.lsp.send_request(

453

WINDOW_WORK_DONE_PROGRESS_CREATE,

454

{"token": token}

455

)

456

457

# Send begin with custom data

458

begin_data = WorkDoneProgressBegin(

459

title=title,

460

cancellable=True,

461

message="Custom progress started"

462

)

463

464

# Add custom fields

465

if custom_data:

466

begin_data.__dict__.update(custom_data)

467

468

self.server.lsp.send_notification(

469

PROGRESS,

470

{"token": token, "value": begin_data}

471

)

472

473

self.active_progress[token] = {

474

"title": title,

475

"custom_data": custom_data

476

}

477

478

def update_custom_progress(

479

self,

480

token: str,

481

percentage: int,

482

custom_fields: dict = None

483

):

484

"""Update progress with custom fields."""

485

486

report_data = WorkDoneProgressReport(

487

percentage=percentage,

488

message=f"Progress: {percentage}%"

489

)

490

491

# Add custom fields

492

if custom_fields:

493

report_data.__dict__.update(custom_fields)

494

495

self.server.lsp.send_notification(

496

PROGRESS,

497

{"token": token, "value": report_data}

498

)

499

500

def end_custom_progress(self, token: str, final_data: dict = None):

501

"""End progress with custom final data."""

502

503

end_data = WorkDoneProgressEnd(

504

message="Custom progress completed"

505

)

506

507

if final_data:

508

end_data.__dict__.update(final_data)

509

510

self.server.lsp.send_notification(

511

PROGRESS,

512

{"token": token, "value": end_data}

513

)

514

515

self.active_progress.pop(token, None)

516

517

# Usage

518

custom_progress = CustomProgress(server)

519

520

@server.command("myServer.customProgressOperation")

521

async def custom_progress_operation(params):

522

token = str(uuid.uuid4())

523

524

await custom_progress.start_custom_progress(

525

token=token,

526

title="Custom Operation",

527

custom_data={"operation_type": "analysis", "version": "2.0"}

528

)

529

530

for i in range(10):

531

await asyncio.sleep(0.1)

532

533

custom_progress.update_custom_progress(

534

token=token,

535

percentage=(i + 1) * 10,

536

custom_fields={"current_step": f"step_{i}", "details": f"Processing item {i}"}

537

)

538

539

custom_progress.end_custom_progress(

540

token=token,

541

final_data={"total_processed": 10, "success": True}

542

)

543

544

return {"completed": True}

545

```