@@ -135,7 +135,20 @@ def report_progress(
135
135
title : str ,
136
136
message : Optional [str ] = None ,
137
137
percentage : Optional [int ] = None ,
138
+ skip_token_initialization : bool = False ,
138
139
) -> Generator [Callable [[str , Optional [int ]], None ], None , None ]:
140
+ """
141
+ Report progress to the editor / client.
142
+
143
+ ``skip_token_initialization` is necessary due to some current
144
+ limitations of our LSP implementation. When `report_progress`
145
+ is used from a synchronous LSP handler, the token initialization
146
+ will time out because we can't receive the response.
147
+
148
+ Many editors will still correctly show the progress messages though, which
149
+ is why we are giving progress users the option to skip the initialization
150
+ of the progress token.
151
+ """
139
152
if self ._config :
140
153
client_supports_progress_reporting = (
141
154
self ._config .capabilities .get ("window" , {}).get ("workDoneProgress" , False )
@@ -144,30 +157,21 @@ def report_progress(
144
157
client_supports_progress_reporting = False
145
158
146
159
if client_supports_progress_reporting :
147
- try :
148
- token = self ._progress_begin (title , message , percentage )
149
- except Exception : # pylint: disable=broad-exception-caught
150
- log .warning (
151
- "There was an error while trying to initialize progress reporting."
152
- "Likely progress reporting was used in a synchronous LSP handler, "
153
- "which is not supported by progress reporting yet." ,
154
- exc_info = True
155
- )
160
+ token = self ._progress_begin (title , message , percentage , skip_token_initialization )
156
161
157
- else :
158
- def progress_message (message : str , percentage : Optional [int ] = None ) -> None :
159
- self ._progress_report (token , message , percentage )
162
+ def progress_message (message : str , percentage : Optional [int ] = None ) -> None :
163
+ self ._progress_report (token , message , percentage )
160
164
161
- try :
162
- yield progress_message
163
- finally :
164
- self ._progress_end (token )
165
+ try :
166
+ yield progress_message
167
+ finally :
168
+ self ._progress_end (token )
165
169
166
- return
170
+ return
167
171
168
172
# FALLBACK:
169
- # If the client doesn't support progress reporting, or if we failed to
170
- # initialize it, we have a dummy method for the caller to use.
173
+ # If the client doesn't support progress reporting, we have a dummy method
174
+ # for the caller to use.
171
175
def dummy_progress_message (message : str , percentage : Optional [int ] = None ) -> None :
172
176
# pylint: disable=unused-argument
173
177
pass
@@ -179,10 +183,23 @@ def _progress_begin(
179
183
title : str ,
180
184
message : Optional [str ] = None ,
181
185
percentage : Optional [int ] = None ,
186
+ skip_token_initialization : bool = False ,
182
187
) -> str :
183
188
token = str (uuid .uuid4 ())
184
189
185
- self ._endpoint .request (self .M_INITIALIZE_PROGRESS , {'token' : token }).result (timeout = 1.0 )
190
+ if not skip_token_initialization :
191
+ try :
192
+ self ._endpoint .request (self .M_INITIALIZE_PROGRESS , {'token' : token }).result (timeout = 1.0 )
193
+ except Exception : # pylint: disable=broad-exception-caught
194
+ log .warning (
195
+ "There was an error while trying to initialize progress reporting."
196
+ "Likely progress reporting was used in a synchronous LSP handler, "
197
+ "which is not supported by progress reporting yet. "
198
+ "To prevent waiting for the timeout you can set "
199
+ "`skip_token_initialization=True`. "
200
+ "Not every editor will show progress then, but many will." ,
201
+ exc_info = True
202
+ )
186
203
187
204
value = {
188
205
"kind" : "begin" ,
0 commit comments