@@ -135,7 +135,20 @@ def report_progress(
135135 title : str ,
136136 message : Optional [str ] = None ,
137137 percentage : Optional [int ] = None ,
138+ skip_token_initialization : bool = False ,
138139 ) -> 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+ """
139152 if self ._config :
140153 client_supports_progress_reporting = (
141154 self ._config .capabilities .get ("window" , {}).get ("workDoneProgress" , False )
@@ -144,30 +157,21 @@ def report_progress(
144157 client_supports_progress_reporting = False
145158
146159 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 )
156161
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 )
160164
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 )
165169
166- return
170+ return
167171
168172 # 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.
171175 def dummy_progress_message (message : str , percentage : Optional [int ] = None ) -> None :
172176 # pylint: disable=unused-argument
173177 pass
@@ -179,10 +183,23 @@ def _progress_begin(
179183 title : str ,
180184 message : Optional [str ] = None ,
181185 percentage : Optional [int ] = None ,
186+ skip_token_initialization : bool = False ,
182187 ) -> str :
183188 token = str (uuid .uuid4 ())
184189
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+ )
186203
187204 value = {
188205 "kind" : "begin" ,
0 commit comments