Repository: sublimelsp/LSP

Issue:
Ready lock timeout with slow Metals 0.8.0
On my laptop, it seems to take a while for *Metals* 0.8.0 to start up and report back to the LSP plugin. The 3-second `ACQUIRE_READY_LOCK_TIMEOUT` expires and the LSP plugin tells *Metals* to shut down - right after it has come up.

See the log below, where the `shutdown` message is sent before the response to the `initialize` message is received.

Changing the timeout to 5 seconds fixed the issue on my machine. Maybe make the timeout configurable in the LSP plugin settings and by default be more generous than 3 seconds?

* OS and language server - Ubuntu 18.04 with *Metals* 0.8.0.

* How you installed LSP (Package Control or from git?) - Latest from GitHub.

* Minimal reproduction steps - Open a LSP-enabled Scala project.

* Log:
```
:: --> metals initialize(1): {...}
metals: INFO  logging to file /home/user/foobar/.metals/metals.log
metals: INFO  started: Metals version 0.8.0 in workspace '/home/user/foobar'
metals: INFO  time: initialize in 1.76s
metals: INFO  shutting down Metals
:: --> metals shutdown(2): None
:: <-- metals 1: {...}
:: --> metals initialized: {}
:: --> metals textDocument/didOpen
```


Discussion:
Looks like commit https://github.com/sublimelsp/LSP/commit/e4848e9e158c5038b9678c1b3b96a6262aaebbeb introduced the timeout on January 5.
Thanks for this report. I have something in the pipeline to remove the need for this lock. In the meantime, would you be willing to experiment with that ACQUIRE_READY_LOCK_TIMEOUT variable in plugin/core/sessions.py to see what works for you?

Edit: I see now that 5 seconds works for you.

Resolving patch:
diff --git a/plugin/core/sessions.py b/plugin/core/sessions.py
--- a/plugin/core/sessions.py
+++ b/plugin/core/sessions.py
@@ -3,6 +3,7 @@
 from .protocol import completion_item_kinds, symbol_kinds, WorkspaceFolder, Request, Notification
 from .protocol import TextDocumentSyncKindNone
 from .rpc import Client, attach_stdio_client, Response
+from .settings import settings as global_settings
 from .transports import start_tcp_transport, start_tcp_listener, TCPTransport, Transport
 from .types import ClientConfig, Settings
 from .typing import Callable, Dict, Any, Optional, List, Tuple, Generator
@@ -11,9 +12,6 @@
 import threading
 
 
-ACQUIRE_READY_LOCK_TIMEOUT = 3
-
-
 def get_initialize_params(workspace_folders: List[WorkspaceFolder], designated_folder: Optional[WorkspaceFolder],
                           config: ClientConfig) -> dict:
     initializeParams = {
@@ -110,7 +108,7 @@ class InitializeError(Exception):
 
     def __init__(self, session: 'Session') -> None:
         super().__init__("{} did not respond to the initialize request within {} seconds".format(
-            session.config.name, ACQUIRE_READY_LOCK_TIMEOUT))
+            session.config.name, global_settings.initialize_timeout))
         self.session = session
 
 
@@ -188,7 +186,7 @@ def should_notify_did_close(self) -> bool:
 
     @contextmanager
     def acquire_timeout(self) -> Generator[None, None, None]:
-        acquired = self.ready_lock.acquire(True, ACQUIRE_READY_LOCK_TIMEOUT)
+        acquired = self.ready_lock.acquire(True, global_settings.initialize_timeout)
         if not acquired:
             raise InitializeError(self)
         yield
diff --git a/plugin/core/settings.py b/plugin/core/settings.py
--- a/plugin/core/settings.py
+++ b/plugin/core/settings.py
@@ -79,6 +79,7 @@ def update_settings(settings: Settings, settings_obj: sublime.Settings) -> None:
     settings.completion_hint_type = read_str_setting(settings_obj, "completion_hint_type", "auto")
     settings.show_references_in_quick_panel = read_bool_setting(settings_obj, "show_references_in_quick_panel", False)
     settings.disabled_capabilities = read_array_setting(settings_obj, "disabled_capabilities", [])
+    settings.initialize_timeout = read_int_setting(settings_obj, "initialize_timeout", 3)
     settings.log_debug = read_bool_setting(settings_obj, "log_debug", False)
     settings.log_server = read_bool_setting(settings_obj, "log_server", True)
     settings.log_stderr = read_bool_setting(settings_obj, "log_stderr", False)
diff --git a/plugin/core/types.py b/plugin/core/types.py
--- a/plugin/core/types.py
+++ b/plugin/core/types.py
@@ -26,6 +26,7 @@ def __init__(self) -> None:
         self.completion_hint_type = "auto"
         self.show_references_in_quick_panel = False
         self.disabled_capabilities = []  # type: List[str]
+        self.initialize_timeout = 3
         self.log_debug = True
         self.log_server = True
         self.log_stderr = False


Test patch:
diff --git a/tests/test_session.py b/tests/test_session.py
--- a/tests/test_session.py
+++ b/tests/test_session.py
@@ -1,6 +1,7 @@
-from LSP.plugin.core.protocol import WorkspaceFolder
 from LSP.plugin.core.protocol import TextDocumentSyncKindFull, TextDocumentSyncKindNone, TextDocumentSyncKindIncremental
-from LSP.plugin.core.sessions import create_session, Session, InitializeError, ACQUIRE_READY_LOCK_TIMEOUT
+from LSP.plugin.core.protocol import WorkspaceFolder
+from LSP.plugin.core.sessions import create_session, Session, InitializeError
+from LSP.plugin.core.settings import settings as global_settings
 from LSP.plugin.core.types import ClientConfig
 from LSP.plugin.core.types import Settings
 from LSP.plugin.core.typing import Callable, Optional
@@ -102,7 +103,7 @@ def test_initialize_failure(self):
 
         def async_response(f: Callable[[], None]) -> None:
             # resolve the request one second after the timeout triggers (so it's always too late).
-            timeout_ms = 1000 * (ACQUIRE_READY_LOCK_TIMEOUT + 1)
+            timeout_ms = 1000 * (global_settings.initialize_timeout + 1)
             sublime.set_timeout(f, timeout_ms=timeout_ms)
 
         client = MockClient(async_response=async_response)