Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure global settings are passed to the server. #284

Merged
merged 1 commit into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 56 additions & 16 deletions bundled/tool/lsp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()))
Expand Down Expand Up @@ -418,26 +424,36 @@ 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",
"refactor": "Hint",
"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

Expand All @@ -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)]


# *****************************************************
Expand Down
5 changes: 3 additions & 2 deletions src/common/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -109,6 +109,7 @@ export async function restartServer(
outputChannel,
{
settings: await getExtensionSettings(serverId, true),
globalSettings: await getGlobalSettings(serverId, false),
},
workspaceSetting,
);
Expand Down
32 changes: 31 additions & 1 deletion src/common/settings.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -136,6 +136,36 @@ export async function getWorkspaceSettings(
return workspaceSetting;
}

function getGlobalValue<T>(config: WorkspaceConfiguration, key: string, defaultValue: T): T {
const inspect = config.inspect<T>(key);
return inspect?.globalValue ?? inspect?.defaultValue ?? defaultValue;
}

export async function getGlobalSettings(namespace: string, includeInterpreter?: boolean): Promise<ISettings> {
const config = getConfiguration(namespace);

let interpreter: string[] | undefined = [];
if (includeInterpreter) {
interpreter = getGlobalValue<string[]>(config, 'interpreter', []);
if (interpreter === undefined || interpreter.length === 0) {
interpreter = (await getInterpreterDetails()).path;
}
}

const setting = {
cwd: process.cwd(),
workspace: process.cwd(),
args: getGlobalValue<string[]>(config, 'args', []),
severity: getGlobalValue<Record<string, string>>(config, 'severity', DEFAULT_SEVERITY),
path: getGlobalValue<string[]>(config, 'path', []),
interpreter: interpreter ?? [],
importStrategy: getGlobalValue<string>(config, 'importStrategy', 'fromEnvironment'),
showNotifications: getGlobalValue<string>(config, 'showNotifications', 'off'),
extraPaths: getGlobalValue<string[]>(config, 'extraPaths', []),
};
return setting;
}

export function checkIfConfigurationChanged(e: ConfigurationChangeEvent, namespace: string): boolean {
const settings = [
`${namespace}.args`,
Expand Down
2 changes: 1 addition & 1 deletion src/test/python_tests/lsp_test_client/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ def get_initialization_options():
setting["cwd"] = str(PROJECT_ROOT)
setting["extraPaths"] = []

return {"settings": [setting]}
return {"settings": [setting], "globalSettings": setting}