From 18b1da047c3db538de7eb53af3a542ec00720dcb Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Wed, 22 Feb 2023 12:32:26 -0800 Subject: [PATCH] Ensure global settings are passed to the server. --- bundled/tool/lsp_server.py | 72 ++++++++++++++----- src/common/server.ts | 5 +- src/common/settings.ts | 32 ++++++++- .../python_tests/lsp_test_client/utils.py | 2 +- 4 files changed, 91 insertions(+), 20 deletions(-) diff --git a/bundled/tool/lsp_server.py b/bundled/tool/lsp_server.py index 24cc6487..ff8f2de7 100644 --- a/bundled/tool/lsp_server.py +++ b/bundled/tool/lsp_server.py @@ -40,6 +40,7 @@ def update_sys_path(path_to_add: str, strategy: str) -> None: from pygls import server, uris, workspace WORKSPACE_SETTINGS = {} +GLOBAL_SETTINGS = {} RUNNER = pathlib.Path(__file__).parent / "runner.py" MAX_WORKERS = 5 @@ -343,11 +344,16 @@ def initialize(params: lsp.InitializeParams) -> None: import_strategy = os.getenv("LS_IMPORT_STRATEGY", "useBundled") update_sys_path(os.getcwd(), import_strategy) + GLOBAL_SETTINGS.update(**params.initialization_options.get("globalSettings", {})) + settings = params.initialization_options["settings"] _update_workspace_settings(settings) log_to_output( f"Settings used to run Server:\r\n{json.dumps(settings, indent=4, ensure_ascii=False)}\r\n" ) + log_to_output( + f"Global settings:\r\n{json.dumps(GLOBAL_SETTINGS, indent=4, ensure_ascii=False)}\r\n" + ) # Add extra paths to sys.path setting = _get_settings_by_path(pathlib.Path(os.getcwd())) @@ -418,17 +424,14 @@ def _log_version_info() -> None: # ***************************************************** # Internal functional and settings management APIs. # ***************************************************** -def _update_workspace_settings(settings): - if not settings: - key = os.getcwd() - WORKSPACE_SETTINGS[key] = { - "cwd": key, - "workspaceFS": key, - "workspace": uris.from_fs_path(key), - "path": [], - "interpreter": [sys.executable], - "args": [], - "severity": { +def _get_global_defaults(): + return { + "path": GLOBAL_SETTINGS.get("path", []), + "interpreter": GLOBAL_SETTINGS.get("interpreter", [sys.executable]), + "args": GLOBAL_SETTINGS.get("args", []), + "severity": GLOBAL_SETTINGS.get( + "severity", + { "convention": "Information", "error": "Error", "fatal": "Error", @@ -436,8 +439,21 @@ def _update_workspace_settings(settings): "warning": "Warning", "info": "Information", }, - "importStrategy": "useBundled", - "showNotifications": "off", + ), + "importStrategy": GLOBAL_SETTINGS.get("importStrategy", "useBundled"), + "showNotifications": GLOBAL_SETTINGS.get("showNotifications", "off"), + "extraPaths": GLOBAL_SETTINGS.get("extraPaths", []), + } + + +def _update_workspace_settings(settings): + if not settings: + key = os.getcwd() + WORKSPACE_SETTINGS[key] = { + "cwd": key, + "workspaceFS": key, + "workspace": uris.from_fs_path(key), + **_get_global_defaults(), } return @@ -462,12 +478,36 @@ def _get_settings_by_path(file_path: pathlib.Path): return setting_values[0] +def _get_document_key(document: workspace.Document): + if WORKSPACE_SETTINGS: + document_workspace = pathlib.Path(document.path) + workspaces = {s["workspaceFS"] for s in WORKSPACE_SETTINGS.values()} + + # Find workspace settings for the given file. + while document_workspace != document_workspace.parent: + if str(document_workspace) in workspaces: + return str(document_workspace) + document_workspace = document_workspace.parent + + return None + + def _get_settings_by_document(document: workspace.Document | None): - if len(WORKSPACE_SETTINGS) == 1 or document is None or document.path is None: + if document is None or document.path is None: return list(WORKSPACE_SETTINGS.values())[0] - document_workspace = pathlib.Path(document.path) - return _get_settings_by_path(document_workspace) + key = _get_document_key(document) + if key is None: + # This is either a non-workspace file or there is no workspace. + key = os.fspath(pathlib.Path(document.path).parent) + return { + "cwd": key, + "workspaceFS": key, + "workspace": uris.from_fs_path(key), + **_get_global_defaults(), + } + + return WORKSPACE_SETTINGS[str(key)] # ***************************************************** diff --git a/src/common/server.ts b/src/common/server.ts index 6a915d74..cf6344a1 100644 --- a/src/common/server.ts +++ b/src/common/server.ts @@ -12,11 +12,11 @@ import { import { DEBUG_SERVER_SCRIPT_PATH, SERVER_SCRIPT_PATH } from './constants'; import { traceError, traceInfo, traceVerbose } from './log/logging'; import { getDebuggerPath } from './python'; -import { getExtensionSettings, getWorkspaceSettings, ISettings } from './settings'; +import { getExtensionSettings, getGlobalSettings, getWorkspaceSettings, ISettings } from './settings'; import { getLSClientTraceLevel, getProjectRoot } from './utilities'; import { isVirtualWorkspace } from './vscodeapi'; -export type IInitOptions = { settings: ISettings[] }; +export type IInitOptions = { settings: ISettings[]; globalSettings: ISettings }; async function createServer( projectRoot: WorkspaceFolder, @@ -109,6 +109,7 @@ export async function restartServer( outputChannel, { settings: await getExtensionSettings(serverId, true), + globalSettings: await getGlobalSettings(serverId, false), }, workspaceSetting, ); diff --git a/src/common/settings.ts b/src/common/settings.ts index 737b1020..8991bfe7 100644 --- a/src/common/settings.ts +++ b/src/common/settings.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { ConfigurationChangeEvent, WorkspaceFolder } from 'vscode'; +import { ConfigurationChangeEvent, WorkspaceConfiguration, WorkspaceFolder } from 'vscode'; import { traceLog } from './log/logging'; import { getInterpreterDetails } from './python'; import { getConfiguration, getWorkspaceFolders } from './vscodeapi'; @@ -136,6 +136,36 @@ export async function getWorkspaceSettings( return workspaceSetting; } +function getGlobalValue(config: WorkspaceConfiguration, key: string, defaultValue: T): T { + const inspect = config.inspect(key); + return inspect?.globalValue ?? inspect?.defaultValue ?? defaultValue; +} + +export async function getGlobalSettings(namespace: string, includeInterpreter?: boolean): Promise { + const config = getConfiguration(namespace); + + let interpreter: string[] | undefined = []; + if (includeInterpreter) { + interpreter = getGlobalValue(config, 'interpreter', []); + if (interpreter === undefined || interpreter.length === 0) { + interpreter = (await getInterpreterDetails()).path; + } + } + + const setting = { + cwd: process.cwd(), + workspace: process.cwd(), + args: getGlobalValue(config, 'args', []), + severity: getGlobalValue>(config, 'severity', DEFAULT_SEVERITY), + path: getGlobalValue(config, 'path', []), + interpreter: interpreter ?? [], + importStrategy: getGlobalValue(config, 'importStrategy', 'fromEnvironment'), + showNotifications: getGlobalValue(config, 'showNotifications', 'off'), + extraPaths: getGlobalValue(config, 'extraPaths', []), + }; + return setting; +} + export function checkIfConfigurationChanged(e: ConfigurationChangeEvent, namespace: string): boolean { const settings = [ `${namespace}.args`, diff --git a/src/test/python_tests/lsp_test_client/utils.py b/src/test/python_tests/lsp_test_client/utils.py index 39c4d661..202d43ee 100644 --- a/src/test/python_tests/lsp_test_client/utils.py +++ b/src/test/python_tests/lsp_test_client/utils.py @@ -66,4 +66,4 @@ def get_initialization_options(): setting["cwd"] = str(PROJECT_ROOT) setting["extraPaths"] = [] - return {"settings": [setting]} + return {"settings": [setting], "globalSettings": setting}