0
# Feature Registration and Decorators
1
2
Decorator-based system for registering LSP features, custom commands, and thread execution patterns with automatic capability registration and declarative programming patterns.
3
4
## Capabilities
5
6
### Feature Registration
7
8
Decorator for registering Language Server Protocol features with automatic capability negotiation and method binding.
9
10
```python { .api }
11
def feature(
12
self,
13
method_name: str,
14
options: Any = None
15
) -> Callable[[F], F]:
16
"""
17
Decorator to register LSP features.
18
19
Parameters:
20
- method_name: str - LSP method name (e.g., 'textDocument/completion')
21
- options: Any - Feature-specific options (e.g., CompletionOptions, HoverOptions)
22
23
Returns:
24
Decorator function that registers the decorated function as a feature handler
25
"""
26
27
@lsp_method(method_name: str) -> Callable[[F], F]:
28
"""
29
Low-level decorator for registering LSP method handlers.
30
31
Parameters:
32
- method_name: str - LSP method name
33
34
Returns:
35
Decorator function for method registration
36
"""
37
```
38
39
### Command Registration
40
41
Decorator for registering custom server commands that can be invoked by clients.
42
43
```python { .api }
44
def command(self, command_name: str) -> Callable[[F], F]:
45
"""
46
Decorator to register custom commands.
47
48
Parameters:
49
- command_name: str - Unique command identifier
50
51
Returns:
52
Decorator function that registers the decorated function as a command handler
53
"""
54
```
55
56
### Thread Execution
57
58
Decorator for executing functions in background threads to avoid blocking the main event loop.
59
60
```python { .api }
61
def thread(self) -> Callable[[F], F]:
62
"""
63
Decorator to execute function in thread pool.
64
65
Returns:
66
Decorator function that wraps the decorated function for thread execution
67
"""
68
```
69
70
### Feature Manager
71
72
Internal feature management system for organizing registered features and commands.
73
74
```python { .api }
75
class FeatureManager:
76
def feature(
77
self,
78
method_name: str,
79
options: Any = None
80
) -> Callable: ...
81
82
def command(self, command_name: str) -> Callable: ...
83
84
def thread(self) -> Callable: ...
85
86
@property
87
def features(self) -> Dict: ...
88
89
@property
90
def commands(self) -> Dict: ...
91
92
@property
93
def feature_options(self) -> Dict: ...
94
```
95
96
## Usage Examples
97
98
### Basic Feature Registration
99
100
```python
101
from pygls.server import LanguageServer
102
from lsprotocol.types import (
103
TEXT_DOCUMENT_COMPLETION,
104
TEXT_DOCUMENT_HOVER,
105
CompletionItem,
106
CompletionList,
107
CompletionParams,
108
Hover,
109
HoverParams,
110
MarkupContent,
111
MarkupKind
112
)
113
114
server = LanguageServer("example-server", "1.0.0")
115
116
# Register completion feature
117
@server.feature(TEXT_DOCUMENT_COMPLETION)
118
def completions(params: CompletionParams):
119
document = server.workspace.get_document(params.text_document.uri)
120
items = [
121
CompletionItem(label="example"),
122
CompletionItem(label="sample")
123
]
124
return CompletionList(is_incomplete=False, items=items)
125
126
# Register hover feature
127
@server.feature(TEXT_DOCUMENT_HOVER)
128
def hover(params: HoverParams):
129
return Hover(
130
contents=MarkupContent(
131
kind=MarkupKind.Markdown,
132
value="**Example hover text**"
133
)
134
)
135
```
136
137
### Feature with Options
138
139
```python
140
from lsprotocol.types import (
141
TEXT_DOCUMENT_COMPLETION,
142
CompletionOptions,
143
CompletionParams
144
)
145
146
# Register completion with trigger characters
147
@server.feature(
148
TEXT_DOCUMENT_COMPLETION,
149
CompletionOptions(trigger_characters=['.', '::'])
150
)
151
def completions_with_triggers(params: CompletionParams):
152
# Handle completion with trigger character context
153
trigger_char = params.context.trigger_character if params.context else None
154
155
if trigger_char == '.':
156
# Provide member completions
157
return CompletionList(items=[
158
CompletionItem(label="method1"),
159
CompletionItem(label="property1")
160
])
161
elif trigger_char == '::':
162
# Provide namespace completions
163
return CompletionList(items=[
164
CompletionItem(label="Class1"),
165
CompletionItem(label="Function1")
166
])
167
168
return CompletionList(items=[])
169
```
170
171
### Custom Commands
172
173
```python
174
from lsprotocol.types import WORKSPACE_EXECUTE_COMMAND
175
176
# Register custom command
177
@server.command("myServer.customCommand")
178
def custom_command(params):
179
# Access command arguments
180
args = params.arguments if params.arguments else []
181
182
# Perform custom logic
183
result = {
184
"message": "Command executed successfully",
185
"arguments": args,
186
"timestamp": time.time()
187
}
188
189
return result
190
191
# Register command that shows configuration
192
@server.command("myServer.showConfiguration")
193
async def show_config(params):
194
# Get configuration from client
195
config = await server.lsp.send_request(
196
"workspace/configuration",
197
{"items": [{"section": "myServer"}]}
198
)
199
200
return {"configuration": config}
201
```
202
203
### Thread Execution
204
205
```python
206
import time
207
208
# Long-running operation in thread
209
@server.command("myServer.longOperation")
210
@server.thread()
211
def long_operation(params):
212
# This runs in a background thread
213
time.sleep(5) # Simulate long-running work
214
return {"result": "Operation completed"}
215
216
# Blocking I/O operation in thread
217
@server.feature(TEXT_DOCUMENT_HOVER)
218
@server.thread()
219
def hover_with_file_io(params: HoverParams):
220
# File I/O that might block
221
with open("some_file.txt", "r") as f:
222
content = f.read()
223
224
return Hover(
225
contents=MarkupContent(
226
kind=MarkupKind.PlainText,
227
value=f"File content: {content[:100]}..."
228
)
229
)
230
```
231
232
### Advanced Feature Registration
233
234
```python
235
from lsprotocol.types import (
236
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
237
SemanticTokensLegend,
238
SemanticTokensOptions,
239
SemanticTokensParams
240
)
241
242
# Complex feature with comprehensive options
243
@server.feature(
244
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
245
SemanticTokensOptions(
246
legend=SemanticTokensLegend(
247
token_types=['keyword', 'string', 'number', 'comment'],
248
token_modifiers=['deprecated', 'readonly']
249
),
250
full=True,
251
range=True
252
)
253
)
254
def semantic_tokens(params: SemanticTokensParams):
255
document = server.workspace.get_document(params.text_document.uri)
256
257
# Parse document and generate semantic tokens
258
tokens = [] # Build semantic token data
259
260
return {"data": tokens}
261
```
262
263
### Error Handling in Features
264
265
```python
266
from pygls.exceptions import FeatureRequestError
267
from lsprotocol.types import TEXT_DOCUMENT_DEFINITION
268
269
@server.feature(TEXT_DOCUMENT_DEFINITION)
270
def goto_definition(params):
271
try:
272
document = server.workspace.get_document(params.text_document.uri)
273
# Definition finding logic here
274
275
if not definition_found:
276
return None # No definition found
277
278
return definition_location
279
280
except Exception as e:
281
# Convert to LSP error
282
raise FeatureRequestError(f"Failed to find definition: {str(e)}")
283
```
284
285
### Dynamic Feature Registration
286
287
```python
288
from pygls.protocol import LanguageServerProtocol
289
290
class CustomProtocol(LanguageServerProtocol):
291
def lsp_initialize(self, params):
292
result = super().lsp_initialize(params)
293
294
# Conditionally register features based on client capabilities
295
if params.capabilities.text_document and params.capabilities.text_document.completion:
296
self.register_dynamic_completion()
297
298
return result
299
300
def register_dynamic_completion(self):
301
# Register completion feature dynamically
302
@self.feature(TEXT_DOCUMENT_COMPLETION)
303
def dynamic_completion(params):
304
return CompletionList(items=[
305
CompletionItem(label="dynamically_registered")
306
])
307
```