Repository: sublimelsp/LSP

Issue:
AttributeError: 'int' object has no attribute 'startswith'
**Describe the bug**
LSP.sublime-package\plugin/core/sessions.py", line 2031, in m___progress
    if token.startswith(_PARTIAL_RESULT_PROGRESS_PREFIX):
AttributeError: 'int' object has no attribute 'startswith'

**To Reproduce**
Steps to reproduce the behavior:
1. Install `LSP-lua`
2. restart sublime
3. Lot of err comes

**Expected behavior**
No err spawn

**Screenshots**
```
Error handling notification
Traceback (most recent call last):
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2245, in on_payload
    handler(result)
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2031, in m___progress
    if token.startswith(_PARTIAL_RESULT_PROGRESS_PREFIX):
AttributeError: 'int' object has no attribute 'startswith'

Error handling notification
Traceback (most recent call last):
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2245, in on_payload
    handler(result)
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2031, in m___progress
    if token.startswith(_PARTIAL_RESULT_PROGRESS_PREFIX):
AttributeError: 'int' object has no attribute 'startswith'

Error handling notification
Traceback (most recent call last):
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2245, in on_payload
    handler(result)
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2031, in m___progress
    if token.startswith(_PARTIAL_RESULT_PROGRESS_PREFIX):
AttributeError: 'int' object has no attribute 'startswith'

Error handling notification
Traceback (most recent call last):
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2245, in on_payload
    handler(result)
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2031, in m___progress
    if token.startswith(_PARTIAL_RESULT_PROGRESS_PREFIX):
AttributeError: 'int' object has no attribute 'startswith'

Error handling notification
Traceback (most recent call last):
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2245, in on_payload
    handler(result)
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2031, in m___progress
    if token.startswith(_PARTIAL_RESULT_PROGRESS_PREFIX):
AttributeError: 'int' object has no attribute 'startswith'

Error handling notification
Traceback (most recent call last):
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2245, in on_payload
    handler(result)
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2031, in m___progress
    if token.startswith(_PARTIAL_RESULT_PROGRESS_PREFIX):
AttributeError: 'int' object has no attribute 'startswith'

Error handling notification
Traceback (most recent call last):
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2245, in on_payload
    handler(result)
  File "C:\Users\jakit\AppData\Roaming\Sublime Text\Installed Packages\LSP.sublime-package\plugin/core/sessions.py", line 2031, in m___progress
    if token.startswith(_PARTIAL_RESULT_PROGRESS_PREFIX):
AttributeError: 'int' object has no attribute 'startswith'
```

**Environment (please complete the following information):**
- OS: Windows 11 22h2
- Sublime Text version: latest
- LSP version: latest from packagecontrol
- Language servers used: lua



Discussion:

Resolving patch:
diff --git a/plugin/core/sessions.py b/plugin/core/sessions.py
--- a/plugin/core/sessions.py
+++ b/plugin/core/sessions.py
@@ -52,6 +52,8 @@
 from .protocol import Notification
 from .protocol import PrepareSupportDefaultBehavior
 from .protocol import PreviousResultId
+from .protocol import ProgressParams
+from .protocol import ProgressToken
 from .protocol import PublishDiagnosticsParams
 from .protocol import RegistrationParams
 from .protocol import Range
@@ -67,6 +69,10 @@
 from .protocol import TokenFormat
 from .protocol import UnregistrationParams
 from .protocol import WindowClientCapabilities
+from .protocol import WorkDoneProgressBegin
+from .protocol import WorkDoneProgressCreateParams
+from .protocol import WorkDoneProgressEnd
+from .protocol import WorkDoneProgressReport
 from .protocol import WorkspaceClientCapabilities
 from .protocol import WorkspaceDiagnosticParams
 from .protocol import WorkspaceDiagnosticReport
@@ -1233,7 +1239,7 @@ def __init__(self, manager: Manager, logger: Logger, workspace_folders: List[Wor
         self._workspace_folders = workspace_folders
         self._session_views = WeakSet()  # type: WeakSet[SessionViewProtocol]
         self._session_buffers = WeakSet()  # type: WeakSet[SessionBufferProtocol]
-        self._progress = {}  # type: Dict[str, Optional[WindowProgressReporter]]
+        self._progress = {}  # type: Dict[ProgressToken, Optional[WindowProgressReporter]]
         self._watcher_impl = get_file_watcher_implementation()
         self._static_file_watchers = []  # type: List[FileWatcher]
         self._dynamic_file_watchers = {}  # type: Dict[str, List[FileWatcher]]
@@ -2012,7 +2018,7 @@ def success(b: Union[None, bool, sublime.View]) -> None:
             # TODO: ST API does not allow us to say "do not focus this new view"
             self.open_uri_async(uri, params.get("selection")).then(success)
 
-    def m_window_workDoneProgress_create(self, params: Any, request_id: Any) -> None:
+    def m_window_workDoneProgress_create(self, params: WorkDoneProgressCreateParams, request_id: Any) -> None:
         """handles the window/workDoneProgress/create request"""
         self._progress[params['token']] = None
         self.send_response(Response(request_id, None))
@@ -2026,7 +2032,7 @@ def _invoke_views(self, request: Request, method: str, *args: Any) -> None:
             for sv in self.session_views_async():
                 getattr(sv, method)(*args)
 
-    def _create_window_progress_reporter(self, token: str, value: Dict[str, Any]) -> None:
+    def _create_window_progress_reporter(self, token: ProgressToken, value: WorkDoneProgressBegin) -> None:
         self._progress[token] = WindowProgressReporter(
             window=self.window,
             key="lspprogress{}{}".format(self.config.name, token),
@@ -2034,60 +2040,65 @@ def _create_window_progress_reporter(self, token: str, value: Dict[str, Any]) ->
             message=value.get("message")
         )
 
-    def m___progress(self, params: Any) -> None:
+    def m___progress(self, params: ProgressParams) -> None:
         """handles the $/progress notification"""
         token = params['token']
         value = params['value']
         # Partial Result Progress
         # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#partialResults
-        if token.startswith(_PARTIAL_RESULT_PROGRESS_PREFIX):
+        if isinstance(token, str) and token.startswith(_PARTIAL_RESULT_PROGRESS_PREFIX):
             request_id = int(token[len(_PARTIAL_RESULT_PROGRESS_PREFIX):])
             request = self._response_handlers[request_id][0]
             if request.method == "workspace/diagnostic":
-                self._on_workspace_diagnostics_async(value, reset_pending_response=False)
+                self._on_workspace_diagnostics_async(
+                    cast(WorkspaceDiagnosticReport, value), reset_pending_response=False)
             return
         # Work Done Progress
         # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workDoneProgress
-        kind = value['kind']
-        if token not in self._progress:
-            # If the token is not in the _progress map then that could mean two things:
-            #
-            # 1) The server is reporting on our client-initiated request progress. In that case, the progress token
-            #    should be of the form $_WORK_DONE_PROGRESS_PREFIX$RequestId. We try to parse it, and if it succeeds,
-            #    we can delegate to the appropriate session view instances.
-            #
-            # 2) The server is not spec-compliant and reports progress using server-initiated progress but didn't
-            #    call window/workDoneProgress/create before hand. In that case, we check the 'kind' field of the
-            #    progress data. If the 'kind' field is 'begin', we set up a progress reporter anyway.
-            try:
-                request_id = int(token[len(_WORK_DONE_PROGRESS_PREFIX):])
-                request = self._response_handlers[request_id][0]
-                self._invoke_views(request, "on_request_progress", request_id, params)
-                return
-            except (IndexError, ValueError, KeyError):
-                # The parse failed so possibility (1) is apparently not applicable. At this point we may still be
-                # dealing with possibility (2).
-                if kind == 'begin':
-                    # We are dealing with possibility (2), so create the progress reporter now.
-                    self._create_window_progress_reporter(token, value)
+        if isinstance(value, dict) and 'kind' in value:
+            kind = value['kind']
+            if token not in self._progress:
+                # If the token is not in the _progress map then that could mean two things:
+                #
+                # 1) The server is reporting on our client-initiated request progress. In that case, the progress token
+                #    should be of the form $_WORK_DONE_PROGRESS_PREFIX$RequestId. We try to parse it, and if it
+                #    succeeds, we can delegate to the appropriate session view instances.
+                #
+                # 2) The server is not spec-compliant and reports progress using server-initiated progress but didn't
+                #    call window/workDoneProgress/create before hand. In that case, we check the 'kind' field of the
+                #    progress data. If the 'kind' field is 'begin', we set up a progress reporter anyway.
+                try:
+                    request_id = int(token[len(_WORK_DONE_PROGRESS_PREFIX):])  # type: ignore
+                    request = self._response_handlers[request_id][0]
+                    self._invoke_views(request, "on_request_progress", request_id, params)
                     return
-                pass
-            debug('unknown $/progress token: {}'.format(token))
-            return
-        if kind == 'begin':
-            self._create_window_progress_reporter(token, value)
-        elif kind == 'report':
-            progress = self._progress[token]
-            assert isinstance(progress, WindowProgressReporter)
-            progress(value.get("message"), value.get("percentage"))
-        elif kind == 'end':
-            progress = self._progress.pop(token)
-            assert isinstance(progress, WindowProgressReporter)
-            title = progress.title
-            progress = None
-            message = value.get('message')
-            if message:
-                self.window.status_message(title + ': ' + message)
+                except (TypeError, IndexError, ValueError, KeyError):
+                    # The parse failed so possibility (1) is apparently not applicable. At this point we may still be
+                    # dealing with possibility (2).
+                    if kind == 'begin':
+                        # We are dealing with possibility (2), so create the progress reporter now.
+                        value = cast(WorkDoneProgressBegin, value)
+                        self._create_window_progress_reporter(token, value)
+                        return
+                debug('unknown $/progress token: {}'.format(token))
+                return
+            if kind == 'begin':
+                value = cast(WorkDoneProgressBegin, value)
+                self._create_window_progress_reporter(token, value)
+            elif kind == 'report':
+                value = cast(WorkDoneProgressReport, value)
+                progress = self._progress[token]
+                assert isinstance(progress, WindowProgressReporter)
+                progress(value.get("message"), value.get("percentage"))
+            elif kind == 'end':
+                value = cast(WorkDoneProgressEnd, value)
+                progress = self._progress.pop(token)
+                assert isinstance(progress, WindowProgressReporter)
+                title = progress.title
+                progress = None
+                message = value.get('message')
+                if message:
+                    self.window.status_message(title + ': ' + message)
 
     # --- shutdown dance -----------------------------------------------------------------------------------------------
 


Test patch: