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

Add reporting scope experimental setting #108

Merged
merged 2 commits into from
Aug 9, 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
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,11 @@
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[python]": {
"diffEditor.ignoreTrimWhitespace": false,
"editor.formatOnSave": true,
"editor.wordBasedSuggestions": false,
"editor.defaultFormatter": "ms-python.black-formatter"
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ If you want to disable mypy, you can [disable this extension](https://code.visua
| mypy-type-checker.interpreter | `[]` | Path to a python interpreter to use to run the linter server. |
| mypy-type-checker.importStrategy | `useBundled` | Setting to choose where to load `mypy` from. `useBundled` picks mypy bundled with the extension. `fromEnvironment` uses `mypy` available in the environment. |
| mypy-type-checker.showNotifications | `off` | Setting to control when a notification is shown. |
| mypy-type-checker.reportingScope | `file` | (experimental) Setting to control if problems are reported for files open in the editor (`file`) or for the entire workspace (`workspace`). |

## Commands

Expand Down
59 changes: 36 additions & 23 deletions bundled/tool/lsp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,14 @@ def update_sys_path(path_to_add: str, strategy: str) -> None:
def did_open(params: lsp.DidOpenTextDocumentParams) -> None:
"""LSP handler for textDocument/didOpen request."""
document = LSP_SERVER.workspace.get_document(params.text_document.uri)
diagnostics: list[lsp.Diagnostic] = _linting_helper(document)
LSP_SERVER.publish_diagnostics(document.uri, diagnostics)
_linting_helper(document)


@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_DID_SAVE)
def did_save(params: lsp.DidSaveTextDocumentParams) -> None:
"""LSP handler for textDocument/didSave request."""
document = LSP_SERVER.workspace.get_document(params.text_document.uri)
diagnostics: list[lsp.Diagnostic] = _linting_helper(document)
LSP_SERVER.publish_diagnostics(document.uri, diagnostics)
_linting_helper(document)


@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_DID_CLOSE)
Expand All @@ -101,11 +99,14 @@ def did_close(params: lsp.DidCloseTextDocumentParams) -> None:
LSP_SERVER.publish_diagnostics(document.uri, [])


def _linting_helper(document: workspace.Document) -> list[lsp.Diagnostic]:
def _linting_helper(document: workspace.Document) -> None:
try:
extra_args = []

code_workspace = _get_settings_by_document(document)["workspaceFS"]
# deep copy here to prevent accidentally updating global settings.
settings = copy.deepcopy(_get_settings_by_document(document))

code_workspace = settings["workspaceFS"]
if VERSION_TABLE.get(code_workspace, None):
major, minor, _ = VERSION_TABLE[code_workspace]
if (major, minor) >= (0, 991) and sys.version_info >= (3, 8):
Expand All @@ -114,12 +115,21 @@ def _linting_helper(document: workspace.Document) -> list[lsp.Diagnostic]:
result = _run_tool_on_document(document, extra_args=extra_args)
if result and result.stdout:
log_to_output(f"{document.uri} :\r\n{result.stdout}")

# deep copy here to prevent accidentally updating global settings.
settings = copy.deepcopy(_get_settings_by_document(document))
return _parse_output_using_regex(
result.stdout, settings["severity"], document
parse_results = _parse_output_using_regex(
result.stdout, settings["severity"]
)
reportingScope = settings["reportingScope"]
for file_path, diagnostics in parse_results.items():
# skip output from other documents
# (mypy will follow imports, so may include errors found in other
# documents; this is fine/correct, we just need to account for it).
if reportingScope == "file" and utils.is_same_path(
file_path, document.path
):
LSP_SERVER.publish_diagnostics(document.uri, diagnostics)
elif reportingScope == "workspace":
uri = uris.from_fs_path(utils.normalize_path(file_path))
LSP_SERVER.publish_diagnostics(uri, diagnostics)
except Exception:
LSP_SERVER.show_message_log(
f"Linting failed with error:\r\n{traceback.format_exc()}",
Expand All @@ -142,10 +152,10 @@ def _get_group_dict(line: str) -> Optional[Dict[str, str | None]]:


def _parse_output_using_regex(
content: str, severity: Dict[str, str], document: workspace.Document
) -> list[lsp.Diagnostic]:
lines: list[str] = content.splitlines()
diagnostics: list[lsp.Diagnostic] = []
content: str, severity: Dict[str, str]
) -> Dict[str, List[lsp.Diagnostic]]:
lines: List[str] = content.splitlines()
diagnostics: Dict[str, List[lsp.Diagnostic]] = {}

notes = []
see_href = None
Expand All @@ -159,12 +169,7 @@ def _parse_output_using_regex(
if not data:
continue

# skip output from other documents
# (mypy will follow imports, so may include errors found in other
# documents; this is fine/correct, we just need to account for it).
if data["filepath"] != document.path:
continue

filepath = utils.normalize_path(data["filepath"])
type_ = data["type"]
code = data["code"]

Expand Down Expand Up @@ -221,7 +226,10 @@ def _parse_output_using_regex(
code_description=lsp.CodeDescription(href=href) if href else None,
source=TOOL_DISPLAY,
)
diagnostics.append(diagnostic)
if filepath in diagnostics:
diagnostics[filepath].append(diagnostic)
else:
diagnostics[filepath] = [diagnostic]

notes = []
see_href = None
Expand Down Expand Up @@ -379,6 +387,7 @@ def _get_global_defaults():
"importStrategy": GLOBAL_SETTINGS.get("importStrategy", "useBundled"),
"showNotifications": GLOBAL_SETTINGS.get("showNotifications", "off"),
"extraPaths": GLOBAL_SETTINGS.get("extraPaths", []),
"reportingScope": GLOBAL_SETTINGS.get("reportingScope", "file"),
}


Expand Down Expand Up @@ -553,7 +562,11 @@ def _run_tool_on_document(
argv += ["-m", "mypy.dmypy"]
argv += _get_dmypy_args(settings, "run")

argv += TOOL_ARGS + settings["args"] + extra_args + [document.path]
argv += TOOL_ARGS + settings["args"] + extra_args
if settings["reportingScope"] == "file":
argv += [document.path]
else:
argv += [cwd]

log_to_output(" ".join(argv))
log_to_output(f"CWD Server: {cwd}")
Expand Down
17 changes: 17 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,23 @@
},
"type": "array"
},
"mypy-type-checker.reportingScope": {
"default": "file",
"markdownDescription": "%settings.reportingScope.description%",
"enum": [
"file",
"workspace"
],
"markdownEnumDescriptions": [
"%settings.reportingScope.file.description%",
"%settings.reportingScope.workspace.description%"
],
"scope": "resource",
"type": "string",
"tags":[
"experimental"
]
},
"mypy-type-checker.severity": {
"default": {
"error": "Error",
Expand Down
3 changes: 3 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"settings.importStrategy.useBundled.description": "Always use the bundled version of `mypy`.",
"settings.importStrategy.fromEnvironment.description": "Use `mypy` from environment, fallback to bundled version only if `mypy` not available in the environment.",
"settings.interpreter.description": "When set to a path to python executable, extension will use that to launch the server and any subprocess.",
"settings.reportingScope.description": "Defines the scope of the problems reported by the extension.",
"settings.reportingScope.file.description": "Problems are reported for the files open in the editor only.",
"settings.reportingScope.workspace.description": "Problems are reported for files in the workspace.",
"settings.showNotifications.description": "Controls when notifications are shown by this extension.",
"settings.showNotifications.off.description": "All notifications are turned off, any errors or warning are still available in the logs.",
"settings.showNotifications.onError.description": "Notifications are shown only in the case of an error.",
Expand Down
5 changes: 4 additions & 1 deletion src/common/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface ISettings {
importStrategy: string;
showNotifications: string;
extraPaths: string[];
reportingScope: string;
}

export function getExtensionSettings(namespace: string, includeInterpreter?: boolean): Promise<ISettings[]> {
Expand Down Expand Up @@ -150,9 +151,10 @@ export async function getWorkspaceSettings(
severity: config.get<Record<string, string>>('severity', DEFAULT_SEVERITY),
path: resolveVariables(mypyPath, workspace),
interpreter: resolveVariables(interpreter, workspace),
importStrategy: config.get<string>('importStrategy', 'fromEnvironment'),
importStrategy: config.get<string>('importStrategy', 'useBundled'),
showNotifications: config.get<string>('showNotifications', 'off'),
extraPaths: resolveVariables(extraPaths, workspace),
reportingScope: config.get<string>('reportingScope', 'file'),
};
return workspaceSetting;
}
Expand Down Expand Up @@ -183,6 +185,7 @@ export async function getGlobalSettings(namespace: string, includeInterpreter?:
importStrategy: getGlobalValue<string>(config, 'importStrategy', 'useBundled'),
showNotifications: getGlobalValue<string>(config, 'showNotifications', 'off'),
extraPaths: getGlobalValue<string[]>(config, 'extraPaths', []),
reportingScope: config.get<string>('reportingScope', 'file'),
};
return setting;
}
Expand Down
8 changes: 4 additions & 4 deletions src/test/python_tests/lsp_test_client/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

import os

from .constants import PROJECT_ROOT
from .constants import TEST_DATA
from .utils import as_uri, get_initialization_options

VSCODE_DEFAULT_INITIALIZE = {
"processId": os.getpid(),
"clientInfo": {"name": "vscode", "version": "1.45.0"},
"rootPath": str(PROJECT_ROOT),
"rootUri": as_uri(str(PROJECT_ROOT)),
"rootPath": str(TEST_DATA),
"rootUri": as_uri(str(TEST_DATA)),
"capabilities": {
"workspace": {
"applyEdit": True,
Expand Down Expand Up @@ -214,6 +214,6 @@
"window": {"workDoneProgress": True},
},
"trace": "verbose",
"workspaceFolders": [{"uri": as_uri(str(PROJECT_ROOT)), "name": "my_project"}],
"workspaceFolders": [{"uri": as_uri(str(TEST_DATA)), "name": "my_project"}],
"initializationOptions": get_initialization_options(),
}
6 changes: 3 additions & 3 deletions src/test/python_tests/lsp_test_client/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import platform
from random import choice

from .constants import PROJECT_ROOT
from .constants import PROJECT_ROOT, TEST_DATA


def normalizecase(path: str) -> str:
Expand Down Expand Up @@ -67,8 +67,8 @@ def get_initialization_options():
value = properties[prop]["default"]
setting[name] = value

setting["workspace"] = as_uri(str(PROJECT_ROOT))
setting["workspace"] = as_uri(str(TEST_DATA))
setting["interpreter"] = []
setting["cwd"] = str(PROJECT_ROOT)
setting["cwd"] = str(TEST_DATA)

return {"settings": [setting]}
1 change: 0 additions & 1 deletion src/test/python_tests/test_data/sample1/sample.unformatted

This file was deleted.

3 changes: 3 additions & 0 deletions src/test/python_tests/test_data/sample1/sample2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .sample import Foo

x: int = "hello"
Loading