33from __future__ import annotations
44
55import atexit
6- import json
76import os
87import shutil
98import threading
109from pathlib import Path
1110
11+ from .. import config
1212from .client import LSPClient , LSPError
1313
14- # command, language id. Users can override/add servers with
15- # PYTHON_AGENT_HARNESS_LSP_SERVERS as a JSON object keyed by language/extension.
14+ # command, language id. Users can override/add servers via the config
15+ # file's ``lsp.servers`` object (see config.load_lsp_config), keyed by
16+ # file extension; those entries layer on top of this table.
1617DEFAULT_SERVERS : dict [str , tuple [list [str ], str ]] = {
1718 ".py" : (["pyright-langserver" , "--stdio" ], "python" ),
1819 ".pyi" : (["pyright-langserver" , "--stdio" ], "python" ),
3435_LOCK = threading .RLock ()
3536
3637
37- def _load_server_config () -> dict [str , tuple [list [str ], str ]]:
38+ def _load_server_config (
39+ config_path : str | os .PathLike | None = None ,
40+ ) -> dict [str , tuple [list [str ], str ]]:
41+ """Merge the built-in DEFAULT_SERVERS with the config file's overrides.
42+
43+ Config-file entries (``lsp.servers``, parsed into an ``LSPConfig``)
44+ win over the built-ins for the same extension. A missing/empty
45+ section leaves the built-ins intact.
46+ """
3847 result = dict (DEFAULT_SERVERS )
39- raw = os .environ .get ("PYTHON_AGENT_HARNESS_LSP_SERVERS" )
40- if not raw :
41- return result
42- try :
43- data = json .loads (raw )
44- except json .JSONDecodeError :
45- return result
46- if not isinstance (data , dict ):
47- return result
48- for key , value in data .items ():
49- if isinstance (value , dict ) and isinstance (value .get ("command" ), list ):
50- command = [str (x ) for x in value ["command" ]]
51- language_id = str (value .get ("language_id" , key .lstrip ("." )))
52- result [str (key )] = (command , language_id )
48+ for ext , server in config .load_lsp_config (config_path ).servers .items ():
49+ result [ext ] = (server .command , server .language_id )
5350 return result
5451
5552
@@ -73,10 +70,12 @@ def _find_root(path: str, project_dir: str) -> str:
7370 current = current .parent
7471
7572
76- def _server_for (path : str ) -> tuple [str , list [str ], str ] | None :
73+ def _server_for (
74+ path : str , config_path : str | os .PathLike | None = None
75+ ) -> tuple [str , list [str ], str ] | None :
7776 ext = Path (path ).suffix .lower ()
78- config = _load_server_config ()
79- spec = config .get (ext )
77+ config_table = _load_server_config (config_path )
78+ spec = config_table .get (ext )
8079 if spec is None :
8180 return None
8281 command , language_id = spec
@@ -85,8 +84,10 @@ def _server_for(path: str) -> tuple[str, list[str], str] | None:
8584 return ext , command , language_id
8685
8786
88- def get_client (path : str , project_dir : str ) -> tuple [LSPClient , str ]:
89- spec = _server_for (path )
87+ def get_client (
88+ path : str , project_dir : str , config_path : str | os .PathLike | None = None
89+ ) -> tuple [LSPClient , str ]:
90+ spec = _server_for (path , config_path )
9091 if spec is None :
9192 raise LSPError (
9293 f"No LSP server configured or installed for { Path (path ).suffix or 'this' } file type."
0 commit comments