Fixed diagnostics files uri handling, added low-level tests

This commit is contained in:
Michael Panchenko authored and Dominik Jain committed 2026-04-29 23:52:26 +02:00
1 parent d198d67a9f
commit bd7e64e85a
91 files changed
+958 -72

No files matched your search

+3 -1
View File
@@ -79,6 +79,8 @@ downloads/
eggs/
.eggs/
lib/
!test/resources/repos/dart/test_repo/lib/
!test/resources/repos/dart/test_repo/lib/diagnostics_sample.dart
lib64/
parts/
sdist/
@@ -271,4 +273,4 @@ cabal.project.local*
zz-misc/
vue-implementation/
news/news.json
news/news.json
+1
View File
@@ -28,6 +28,7 @@ project_name: "serena"
languages:
- python
- typescript
- clojure
# whether to use project's .gitignore files to ignore files
ignore_all_files_in_gitignore: true
+4 -2
View File
@@ -16,6 +16,7 @@ from serena.tools import (
JetBrainsFindSymbolTool,
JetBrainsGetSymbolsOverviewTool,
JetBrainsInlineSymbol,
JetBrainsRunInspectionsTool,
JetBrainsSafeDeleteTool,
SearchForPatternTool,
)
@@ -35,12 +36,13 @@ if __name__ == "__main__":
overview_tool = agent.get_tool(JetBrainsGetSymbolsOverviewTool)
safe_delete_tool = agent.get_tool(JetBrainsSafeDeleteTool)
inline_symbol = agent.get_tool(JetBrainsInlineSymbol)
diagnostics_in_file_tool = agent.get_tool(GetDiagnosticsForFileTool)
jb_inspections_tool = agent.get_tool(JetBrainsRunInspectionsTool)
result = agent.execute_task(
lambda: overview_tool.apply(
# name_path_pattern="SerenaAgent",
relative_path="src/serena/agent.py",
depth=2,
relative_path="test/resources/repos/clojure/test_repo/src/test_app/diagnostics_sample.clj",
# keep_definition=True,
)
)
@@ -689,7 +689,7 @@ class JetBrainsPluginClient(ToStringMixin):
request_data["startLine"] = start_line
if end_line is not None:
request_data["endLine"] = end_line
return cast(jb.RunInspectionsResponse, self._make_request("POST", "/runInspections", request_data))
return cast(jb.RunInspectionsResponse, self._make_request("POST", "/runInspectionsOnFile", request_data))
def list_inspections(
self,
@@ -32,9 +32,10 @@ class ElmLanguageServer(SolidLanguageServer):
Creates an ElmLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
"""
elm_lsp_executable_path = self._setup_runtime_dependencies(config, solidlsp_settings)
managed_bin_dir = os.path.join(self.ls_resources_dir(solidlsp_settings), "elm-lsp", "node_modules", ".bin")
# Resolve ELM_HOME to absolute path if it's set to a relative path
env = {}
env = {"PATH": managed_bin_dir + os.pathsep + os.environ.get("PATH", "")}
elm_home = os.environ.get("ELM_HOME")
if elm_home:
if not os.path.isabs(elm_home):
@@ -65,9 +66,15 @@ class ElmLanguageServer(SolidLanguageServer):
"""
Setup runtime dependencies for Elm Language Server and return the command to start the server.
"""
# Check if elm-language-server is already installed globally
elm_config = solidlsp_settings.get_ls_specific_settings(Language.ELM)
elm_language_server_version = elm_config.get("elm_language_server_version", "2.8.0")
elm_compiler_version = elm_config.get("elm_compiler_version", "0.19.1-6")
npm_registry = elm_config.get("npm_registry")
# Check if elm-language-server and elm are already installed globally
system_elm_ls = shutil.which("elm-language-server")
if system_elm_ls:
system_elm = shutil.which("elm")
if system_elm_ls and system_elm:
log.info(f"Found system-installed elm-language-server at {system_elm_ls}")
return [system_elm_ls, "--stdio"]
@@ -76,10 +83,6 @@ class ElmLanguageServer(SolidLanguageServer):
assert is_node_installed, "node is not installed or isn't in PATH. Please install NodeJS and try again."
is_npm_installed = shutil.which("npm") is not None
assert is_npm_installed, "npm is not installed or isn't in PATH. Please install npm and try again."
elm_config = solidlsp_settings.get_ls_specific_settings(Language.ELM)
elm_language_server_version = elm_config.get("elm_language_server_version", "2.8.0")
npm_registry = elm_config.get("npm_registry")
deps = RuntimeDependencyCollection(
[
RuntimeDependency(
@@ -88,6 +91,12 @@ class ElmLanguageServer(SolidLanguageServer):
command=build_npm_install_command("@elm-tooling/elm-language-server", elm_language_server_version, npm_registry),
platform_id="any",
),
RuntimeDependency(
id="elm",
description="Elm compiler package",
command=build_npm_install_command("elm", elm_compiler_version, npm_registry),
platform_id="any",
),
]
)
@@ -98,6 +107,10 @@ class ElmLanguageServer(SolidLanguageServer):
log.info(f"Elm Language Server executable not found at {elm_ls_executable_path}. Installing...")
with LogTime("Installation of Elm language server dependencies", logger=log):
deps.install(elm_ls_dir)
elif not system_elm:
log.info("Elm compiler not found on PATH. Installing a managed Elm compiler...")
with LogTime("Installation of Elm compiler dependency", logger=log):
deps.install(elm_ls_dir)
if not os.path.exists(elm_ls_executable_path):
raise FileNotFoundError(
@@ -128,6 +141,7 @@ class ElmLanguageServer(SolidLanguageServer):
"hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]},
"codeAction": {"dynamicRegistration": True},
"rename": {"dynamicRegistration": True},
"publishDiagnostics": {"relatedInformation": True},
},
"workspace": {
"workspaceFolders": True,
@@ -220,6 +220,7 @@ class FortranLanguageServer(SolidLanguageServer):
"formatting": {"dynamicRegistration": True},
"rangeFormatting": {"dynamicRegistration": True},
"codeAction": {"dynamicRegistration": True},
"publishDiagnostics": {"relatedInformation": True},
},
"workspace": {
"workspaceFolders": True,
@@ -124,6 +124,7 @@ class Lean4LanguageServer(SolidLanguageServer):
"documentationFormat": ["markdown", "plaintext"],
},
},
"publishDiagnostics": {"relatedInformation": True},
},
"workspace": {
"workspaceFolders": True,
@@ -777,6 +777,14 @@ class PascalLanguageServer(SolidLanguageServer):
if value:
initialization_options[var] = value
initialization_options.update(
{
"checkSyntax": True,
"publishDiagnostics": True,
"showSyntaxErrors": True,
}
)
initialize_params = {
"locale": "en",
"capabilities": {
@@ -831,6 +839,7 @@ class PascalLanguageServer(SolidLanguageServer):
},
"formatting": {"dynamicRegistration": True},
"rangeFormatting": {"dynamicRegistration": True},
"publishDiagnostics": {"relatedInformation": True},
},
"workspace": {
"workspaceFolders": True,
@@ -148,6 +148,7 @@ class SolidityLanguageServer(SolidLanguageServer):
"dynamicRegistration": True,
"contentFormat": ["markdown", "plaintext"], # type: ignore[list-item]
},
"publishDiagnostics": {"relatedInformation": True},
},
"workspace": {
"workspaceFolders": True,
@@ -89,7 +89,10 @@ class TypeScriptLanguageServer(SolidLanguageServer):
self.server_ready = threading.Event()
self.initialize_searcher_command_available = threading.Event()
# Progress tracking for $/progress notifications (project indexing, etc.)
# tracking asynchronous diagnostics publication
self._published_diagnostics_timeout = 5.0
# tracking project indexing progress
self._progress_lock = threading.Lock()
self._active_progress_tokens: set[str] = set()
self._indexing_complete = threading.Event()
@@ -243,9 +246,11 @@ class TypeScriptLanguageServer(SolidLanguageServer):
"signatureHelp": {"dynamicRegistration": True},
"codeAction": {"dynamicRegistration": True},
"rename": {"dynamicRegistration": True, "prepareSupport": True},
"publishDiagnostics": {"relatedInformation": True},
},
"workspace": {
"workspaceFolders": True,
"configuration": True,
"didChangeConfiguration": {"dynamicRegistration": True},
"symbol": {"dynamicRegistration": True},
},
@@ -293,6 +298,10 @@ class TypeScriptLanguageServer(SolidLanguageServer):
def execute_client_command_handler(params: dict) -> list:
return []
def configuration_handler(params: dict) -> list:
items = params.get("items", [])
return [{} for _ in items]
def do_nothing(params: dict) -> None:
return
@@ -352,6 +361,7 @@ class TypeScriptLanguageServer(SolidLanguageServer):
self._indexing_complete.set()
self.server.on_request("client/registerCapability", register_capability_handler)
self.server.on_request("workspace/configuration", configuration_handler)
self.server.on_notification("window/logMessage", window_log_message)
self.server.on_request("workspace/executeClientCommand", execute_client_command_handler)
self.server.on_request("window/workDoneProgress/create", work_done_progress_create)
@@ -397,6 +407,20 @@ class TypeScriptLanguageServer(SolidLanguageServer):
self.INDEXING_PROGRESS_TIMEOUT,
)
@override
def _get_published_diagnostics_uri(self, request_uri: str) -> str:
if os.name != "nt" or not request_uri.startswith("file:///"):
return request_uri
path_part = request_uri[len("file:///") :]
if len(path_part) >= 2 and path_part[0].isalpha() and path_part[1] == ":":
return f"file:///{path_part[0].lower()}%3A{path_part[2:]}"
return request_uri
@override
def _get_published_diagnostics_wait_timeout(self, pull_diagnostics_failed: bool) -> float:
return self._published_diagnostics_timeout
@override
def _get_wait_time_for_cross_file_referencing(self) -> float:
return 2
@@ -426,6 +426,18 @@ class VueLanguageServer(SolidLanguageServer):
with self._ts_server.open_file(relative_file_path):
return self._ts_server.request_rename_symbol_edit(relative_file_path, line, column, new_name)
@override
def request_text_document_diagnostics(
self,
relative_file_path: str,
start_line: int = 0,
end_line: int = -1,
min_severity: int = 4,
) -> list[ls_types.Diagnostic]:
self._ensure_ls_operational()
assert self._ts_server is not None
return self._ts_server.request_text_document_diagnostics(relative_file_path, start_line, end_line, min_severity)
def _forward_edit_to_ts_server_if_needed(self, relative_file_path: str, edit_fn: Callable[[], object]) -> None:
"""
Calls ``edit_fn`` on the TypeScript server if the file is open there.
@@ -598,6 +610,7 @@ class VueLanguageServer(SolidLanguageServer):
"signatureHelp": {"dynamicRegistration": True},
"codeAction": {"dynamicRegistration": True},
"rename": {"dynamicRegistration": True, "prepareSupport": True},
"publishDiagnostics": {"relatedInformation": True},
},
"workspace": {
"workspaceFolders": True,
+1
View File
@@ -132,6 +132,7 @@ class ZigLanguageServer(SolidLanguageServer):
"dynamicRegistration": True,
"contentFormat": ["markdown", "plaintext"],
},
"publishDiagnostics": {"relatedInformation": True},
},
"workspace": {
"workspaceFolders": True,
+67 -12
View File
@@ -13,7 +13,7 @@ from collections.abc import Hashable, Iterator
from contextlib import contextmanager
from copy import copy
from pathlib import Path, PurePath
from time import perf_counter, sleep
from time import monotonic, perf_counter, sleep
from typing import Any, Self, Union, cast
import pathspec
@@ -754,21 +754,77 @@ class SolidLanguageServer(ABC):
:return: the published diagnostics, or ``None`` if no diagnostics are available.
"""
uri = self._validate_text_document_diagnostics_request(relative_file_path, start_line, end_line, min_severity)
published_uri = self._get_published_diagnostics_uri(uri)
diagnostics: list[ls_types.Diagnostic] | None = None
# keeping the document open
with self.open_file(relative_file_path):
diagnostics = self._wait_for_published_diagnostics(uri=uri, after_generation=after_generation, timeout=timeout)
# falling back to cached diagnostics
if diagnostics is None and allow_cached:
diagnostics = self._get_cached_published_diagnostics(uri)
diagnostics = self._wait_for_relevant_published_diagnostics(
uri=published_uri,
after_generation=after_generation,
timeout=timeout,
allow_cached=allow_cached,
)
if diagnostics is None:
return None
return self._filter_diagnostics(diagnostics, start_line, end_line, min_severity)
def _get_published_diagnostics_uri(self, request_uri: str) -> str:
"""
Gets the URI under which published diagnostics should be looked up.
"""
return request_uri
def _get_published_diagnostics_wait_timeout(self, pull_diagnostics_failed: bool) -> float:
"""
Gets the timeout for waiting on published diagnostics after a diagnostics request.
"""
return 2.5
def _accept_published_diagnostics(self, diagnostics: list[ls_types.Diagnostic]) -> bool:
"""
Determines whether a published diagnostics payload should satisfy the current wait.
"""
return bool(diagnostics)
def _wait_for_relevant_published_diagnostics(
self,
uri: str,
after_generation: int,
timeout: float,
allow_cached: bool = True,
) -> list[ls_types.Diagnostic] | None:
"""
Waits for a published diagnostics payload that is relevant for the current request.
"""
deadline = monotonic() + timeout
current_after_generation = after_generation
while True:
remaining_timeout = deadline - monotonic()
if remaining_timeout <= 0:
break
diagnostics = self._wait_for_published_diagnostics(
uri=uri,
after_generation=current_after_generation,
timeout=remaining_timeout,
)
if diagnostics is None:
break
if self._accept_published_diagnostics(diagnostics):
return diagnostics
current_after_generation = self._get_published_diagnostics_generation(uri)
if allow_cached:
diagnostics = self._get_cached_published_diagnostics(uri)
if diagnostics is not None and self._accept_published_diagnostics(diagnostics):
return diagnostics
return None
def request_text_document_diagnostics(
self,
relative_file_path: str,
@@ -789,7 +845,8 @@ class SolidLanguageServer(ABC):
:return: A list of diagnostics for the file
"""
uri = self._validate_text_document_diagnostics_request(relative_file_path, start_line, end_line, min_severity)
diagnostics_before_request = self._get_published_diagnostics_generation(uri)
published_uri = self._get_published_diagnostics_uri(uri)
diagnostics_before_request = self._get_published_diagnostics_generation(published_uri)
ret: list[ls_types.Diagnostic] | None = None
pull_diagnostics_failed = False
@@ -825,13 +882,11 @@ class SolidLanguageServer(ABC):
ret.append(ls_types.Diagnostic(**new_item))
if not ret:
published_diagnostics = self._wait_for_published_diagnostics(
uri=uri,
published_diagnostics = self._wait_for_relevant_published_diagnostics(
uri=published_uri,
after_generation=diagnostics_before_request,
timeout=2.5 if pull_diagnostics_failed else 0.5,
timeout=self._get_published_diagnostics_wait_timeout(pull_diagnostics_failed),
)
if published_diagnostics is None:
published_diagnostics = self._get_cached_published_diagnostics(uri)
if published_diagnostics is not None:
ret = published_diagnostics
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
if then
echo "$missingGreeting"
fi
@@ -0,0 +1,5 @@
def broken_factory : String
missingGreeting
end
broken_consumer = missingConsumerValue
@@ -0,0 +1,3 @@
String brokenFactory() {
return 'broken'
}
@@ -0,0 +1,10 @@
defmodule DiagnosticsSample do
def broken_factory do
missing_greeting
end
def broken_consumer do
value = broken_factory()
{value, missing_consumer_value}
end
end
@@ -0,0 +1,5 @@
module DiagnosticsSample exposing (brokenFactory)
brokenFactory : String
brokenFactory =
"unterminated
@@ -0,0 +1,5 @@
-module(diagnostics_sample).
-export([broken_factory/0, broken_consumer/0]).
broken_factory() -> MissingGreeting.
broken_consumer() -> MissingConsumerValue.
@@ -0,0 +1,5 @@
program diagnostics_sample
implicit none
integer :: duplicated_value
real :: duplicated_value
end program diagnostics_sample
@@ -0,0 +1,13 @@
package com.example
class DiagnosticsSample {
static String brokenFactory() {
missingGreeting
}
static void brokenConsumer() {
def value = brokenFactory()
println(value)
println(missingConsumerValue)
}
}
@@ -0,0 +1,7 @@
module DiagnosticsSample where
brokenFactory :: String
brokenFactory = missingGreeting
brokenConsumer :: String
brokenConsumer = missingConsumerValue
@@ -0,0 +1,10 @@
class DiagnosticsSample {
static function brokenFactory():String {
return missingGreeting;
}
static function brokenConsumer():Void {
trace(brokenFactory());
trace(missingConsumerValue);
}
}
@@ -0,0 +1,3 @@
float4 main() : SV_Target {
return missingGreeting;
}
@@ -0,0 +1,4 @@
{
"name": "diagnostics",
"value":
}
@@ -0,0 +1,3 @@
function broken_factory(
value = 1
end
@@ -1,6 +1 @@
def brokenFactory : Nat :=
missingGreeting
def brokenConsumer : Nat :=
let value := brokenFactory
value + missingConsumerValue
@@ -0,0 +1,3 @@
function value = diagnostics_sample()
value = missingGreeting;
end
@@ -0,0 +1,4 @@
{
greeting = missingGreeting;
consumer = missingConsumerValue;
}
@@ -0,0 +1 @@
let broken_factory (
@@ -1,3 +1,4 @@
(library
(public_name test_repo)
(name test_repo))
(name test_repo)
(modules test_repo))
@@ -0,0 +1,4 @@
program DiagnosticsSample;
begin
writeln(
end.
@@ -0,0 +1,5 @@
use strict;
use warnings;
print $missingGreeting;
print $missingConsumerValue;
@@ -1,9 +1,4 @@
function Invoke-BrokenFactory {
Invoke-MissingGreeting
}
function Invoke-BrokenConsumer {
$value = Invoke-BrokenFactory
Write-Output $value
Invoke-MissingConsumerValue
if ($true) {
Write-Output "broken"
}
@@ -0,0 +1,9 @@
package com.example
object DiagnosticsSample {
def brokenFactory(): String = missingGreeting
def brokenConsumer(): Unit = {
println(brokenFactory())
println(missingConsumerValue)
}
}
@@ -0,0 +1,7 @@
pragma solidity ^0.8.0;
contract DiagnosticsSample {
function brokenFactory() pure returns (string memory) {
return "broken";
}
}
@@ -11,6 +11,7 @@ let package = Package(
targets: [
.target(
name: "test_repo",
dependencies: []),
dependencies: [],
path: "src"),
]
)
)
@@ -0,0 +1,3 @@
func brokenFactory() -> String {
return
}
@@ -0,0 +1,5 @@
module diagnostics_sample;
initial begin
if (
end
endmodule
@@ -0,0 +1,4 @@
resource "aws_instance" "broken" {
ami =
instance_type = "t2.micro"
}
@@ -0,0 +1,2 @@
[diagnostics]
value =
@@ -0,0 +1,3 @@
<script setup lang="ts">
const value: string =
</script>
@@ -0,0 +1,5 @@
name: diagnostics
value:
- good
-
broken: [
+1 -1
View File
@@ -28,4 +28,4 @@ pub fn build(b: *std.Build) void {
const run_lib_tests = b.addRunArtifact(lib_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_tests.step);
}
}
@@ -0,0 +1,3 @@
pub fn brokenFactory() void {
const value =
}
@@ -29,4 +29,4 @@ pub fn main() !void {
pub fn greeting(name: []const u8) []const u8 {
return std.fmt.allocPrint(std.heap.page_allocator, "Hello, {s}!", .{name}) catch "Hello!";
}
}
@@ -68,13 +68,13 @@
# name: test_delete_symbol[test_case1]
'''
import { ConsoleGreeter, Greeter } from "./formatters";
export function helperFunction() {
const demo = new DemoClass(42);
demo.printValue();
const greeter: Greeter = new ConsoleGreeter();
console.log(greeter.formatGreeting("World"));
}
@@ -521,7 +521,7 @@
# name: test_insert_in_rel_to_symbol[test_case1-after]
'''
import { ConsoleGreeter, Greeter } from "./formatters";
export class DemoClass {
value: number;
constructor(value: number) {
@@ -539,7 +539,7 @@
export function helperFunction() {
const demo = new DemoClass(42);
demo.printValue();
const greeter: Greeter = new ConsoleGreeter();
console.log(greeter.formatGreeting("World"));
}
@@ -555,7 +555,7 @@
# name: test_insert_in_rel_to_symbol[test_case1-before]
'''
import { ConsoleGreeter, Greeter } from "./formatters";
function newFunctionAfterClass(): void {
console.log("This function is after DemoClass.");
}
@@ -573,7 +573,7 @@
export function helperFunction() {
const demo = new DemoClass(42);
demo.printValue();
const greeter: Greeter = new ConsoleGreeter();
console.log(greeter.formatGreeting("World"));
}
@@ -589,7 +589,7 @@
# name: test_insert_in_rel_to_symbol[test_case2-after]
'''
import { ConsoleGreeter, Greeter } from "./formatters";
export class DemoClass {
value: number;
constructor(value: number) {
@@ -603,7 +603,7 @@
export function helperFunction() {
const demo = new DemoClass(42);
demo.printValue();
const greeter: Greeter = new ConsoleGreeter();
console.log(greeter.formatGreeting("World"));
}
@@ -623,7 +623,7 @@
# name: test_insert_in_rel_to_symbol[test_case2-before]
'''
import { ConsoleGreeter, Greeter } from "./formatters";
export class DemoClass {
value: number;
constructor(value: number) {
@@ -641,7 +641,7 @@
export function helperFunction() {
const demo = new DemoClass(42);
demo.printValue();
const greeter: Greeter = new ConsoleGreeter();
console.log(greeter.formatGreeting("World"));
}
@@ -1712,7 +1712,7 @@
# name: test_replace_body[test_case1]
'''
import { ConsoleGreeter, Greeter } from "./formatters";
export class DemoClass {
value: number;
constructor(value: number) {
@@ -1727,7 +1727,7 @@
export function helperFunction() {
const demo = new DemoClass(42);
demo.printValue();
const greeter: Greeter = new ConsoleGreeter();
console.log(greeter.formatGreeting("World"));
}
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.bash
class TestBashDiagnostics:
@pytest.mark.parametrize("language_server", [Language.BASH], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.sh",
(),
min_count=1,
)
+2 -2
View File
@@ -13,8 +13,8 @@ def _test_clojure_cli() -> bool:
CLI_FAIL = _test_clojure_cli()
TEST_APP_PATH = Path("src") / "test_app"
CORE_PATH = str(TEST_APP_PATH / "core.clj").replace("\\", "/")
UTILS_PATH = str(TEST_APP_PATH / "utils.clj").replace("\\", "/")
CORE_PATH = str(TEST_APP_PATH / "core.clj")
UTILS_PATH = str(TEST_APP_PATH / "utils.clj")
def is_clojure_cli_available() -> bool:
@@ -0,0 +1,19 @@
import os
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.clojure
class TestClojureDiagnostics:
@pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
os.path.join("src", "test_app", "diagnostics_sample.clj"),
("missing-greeting", "missing-consumer-value"),
min_count=2,
)
+9
View File
@@ -5,6 +5,12 @@ from solidlsp.ls_types import SymbolKind, UnifiedSymbolInformation
PYTHON_BACKEND_LANGUAGES = [Language.PYTHON, Language.PYTHON_TY]
def is_diagnostics_test_file(relative_path: str) -> bool:
normalized_path = relative_path.replace("\\", "/")
filename = normalized_path.rsplit("/", 1)[-1].lower()
return filename.startswith(("diagnosticssample.", "diagnostics_sample."))
def has_malformed_name(
symbol: UnifiedSymbolInformation,
whitespace_allowed: bool = False,
@@ -36,6 +42,9 @@ def request_all_symbols(language_server: SolidLanguageServer) -> list[UnifiedSym
result: list[UnifiedSymbolInformation] = []
def visit(symbol: UnifiedSymbolInformation) -> None:
relative_path = symbol.get("location", {}).get("relativePath", "")
if relative_path and is_diagnostics_test_file(relative_path):
return
result.append(symbol)
for child in symbol.get("children", []):
visit(child)
+17
View File
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.cpp
class TestCppDiagnostics:
@pytest.mark.parametrize("language_server", [Language.CPP], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.cpp",
(),
min_count=1,
)
@@ -0,0 +1,23 @@
import shutil
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
pytestmark = [
pytest.mark.crystal,
pytest.mark.skipif(shutil.which("crystalline") is None, reason="Crystalline is not installed"),
]
class TestCrystalDiagnostics:
@pytest.mark.parametrize("language_server", [Language.CRYSTAL], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"src/diagnostics_sample.cr",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.csharp
class TestCsharpDiagnostics:
@pytest.mark.parametrize("language_server", [Language.CSHARP], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"DiagnosticsSample.cs",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.dart
class TestDartDiagnostics:
@pytest.mark.parametrize("language_server", [Language.DART], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"lib/diagnostics_sample.dart",
(),
min_count=1,
)
+10
View File
@@ -13,6 +13,7 @@ from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from solidlsp.ls_types import SymbolKind
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics
from . import EXPERT_UNAVAILABLE, EXPERT_UNAVAILABLE_REASON
@@ -138,3 +139,12 @@ class TestElixirBasic:
f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}",
pytrace=False,
)
@pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"lib/diagnostics_sample.ex",
(),
min_count=1,
)
+20
View File
@@ -0,0 +1,20 @@
import shutil
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.elm
@pytest.mark.skipif(shutil.which("node") is None or shutil.which("npm") is None, reason="Elm diagnostics require Node.js and npm")
class TestElmDiagnostics:
@pytest.mark.parametrize("language_server", [Language.ELM], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"DiagnosticsSample.elm",
(),
min_count=1,
)
+10
View File
@@ -10,6 +10,7 @@ import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics
from . import ERLANG_LS_UNAVAILABLE, ERLANG_LS_UNAVAILABLE_REASON
@@ -55,3 +56,12 @@ class TestErlangLanguageServerBasics:
f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}",
pytrace=False,
)
@pytest.mark.parametrize("language_server", [Language.ERLANG], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"src/diagnostics_sample.erl",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.fortran
class TestFortranDiagnostics:
@pytest.mark.parametrize("language_server", [Language.FORTRAN], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.f90",
(),
min_count=1,
)
+8 -15
View File
@@ -9,6 +9,7 @@ from solidlsp.ls_config import Language
from solidlsp.ls_utils import SymbolUtils
from test.conftest import find_identifier_position, get_repo_path, is_ci, language_has_verified_implementation_support
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics
# Currently, most F# tests fail, there seems to be a regression or instability.
@@ -196,21 +197,13 @@ class TestFSharpLanguageServer:
assert isinstance(result["value"], list), "Completions should be a list"
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_diagnostics(self, language_server: SolidLanguageServer) -> None:
"""Test getting diagnostics (errors, warnings) from F# files."""
file_path = os.path.join("Program.fs")
# FsAutoComplete uses publishDiagnostics notifications instead of textDocument/diagnostic requests
# So we'll test that the language server can handle files without crashing
# In real usage, diagnostics would come through the publishDiagnostics notification handler
# Test that we can at least work with the file (open/close cycle)
with language_server.open_file(file_path) as _:
# If we can open and close the file without errors, basic diagnostics support is working
pass
# This is a successful test - FsAutoComplete is working with F# files
assert True, "F# language server can handle files successfully"
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"DiagnosticsSample.fs",
(),
min_count=1,
)
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_bare_symbol_names(self, language_server) -> None:
+17
View File
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.go
class TestGoDiagnostics:
@pytest.mark.parametrize("language_server", [Language.GO], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.go",
("missingGreeting", "missingConsumerValue"),
min_count=2,
)
+10
View File
@@ -9,6 +9,7 @@ from solidlsp.ls_config import Language, LanguageServerConfig
from solidlsp.ls_utils import SymbolUtils
from solidlsp.settings import SolidLSPSettings
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.groovy
@@ -117,3 +118,12 @@ class TestGroovyLanguageServer:
f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}",
pytrace=False,
)
def test_file_diagnostics(self) -> None:
assert self.language_server is not None
assert_file_diagnostics(
self.language_server,
"src/main/groovy/com/example/DiagnosticsSample.groovy",
(),
min_count=1,
)
@@ -21,6 +21,7 @@ from solidlsp.ls import SolidLanguageServer
from solidlsp.ls_config import Language
from solidlsp.ls_types import SymbolKind
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.haskell
@@ -240,3 +241,12 @@ class TestHaskellLanguageServer:
for sym in malformed_symbols
]
pytest.fail(f"Found malformed symbols: {diagnostics}", pytrace=False)
@pytest.mark.parametrize("language_server", [Language.HASKELL], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"src/DiagnosticsSample.hs",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.haxe
class TestHaxeDiagnostics:
@pytest.mark.parametrize("language_server", [Language.HAXE], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"src/DiagnosticsSample.hx",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.hlsl
class TestHlslDiagnostics:
@pytest.mark.parametrize("language_server", [Language.HLSL], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.hlsl",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.java
class TestJavaDiagnostics:
@pytest.mark.parametrize("language_server", [Language.JAVA], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"src/main/java/test_repo/DiagnosticsSample.java",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.json
class TestJsonDiagnostics:
@pytest.mark.parametrize("language_server", [Language.JSON], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.json",
(),
min_count=1,
)
+10
View File
@@ -6,6 +6,7 @@ from solidlsp.ls import SolidLanguageServer
from solidlsp.ls_config import Language
from test.conftest import is_ci
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.skipif(shutil.which("julia") is None and not is_ci, reason="Julia is not available")
@@ -66,3 +67,12 @@ class TestJuliaLanguageServer:
f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}",
pytrace=False,
)
@pytest.mark.parametrize("language_server", [Language.JULIA], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.jl",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.kotlin
class TestKotlinDiagnostics:
@pytest.mark.parametrize("language_server", [Language.KOTLIN], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"src/main/kotlin/test_repo/DiagnosticsSample.kt",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.lean4
class TestLean4Diagnostics:
@pytest.mark.parametrize("language_server", [Language.LEAN4], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"DiagnosticsSample.lean",
(),
min_count=1,
)
+10
View File
@@ -17,6 +17,7 @@ import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics
# Skip all tests if MATLAB is not available
pytestmark = pytest.mark.matlab
@@ -129,3 +130,12 @@ class TestMatlabLanguageServerReferences:
f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}",
pytrace=False,
)
@pytest.mark.parametrize("language_server", [Language.MATLAB], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.m",
(),
min_count=1,
)
+10
View File
@@ -13,6 +13,7 @@ from solidlsp.ls_config import Language
from solidlsp.ls_types import SymbolKind
from test.conftest import is_ci
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics
# Skip all Nix tests on Windows as Nix doesn't support Windows
pytestmark = pytest.mark.skipif(platform.system() == "Windows", reason="Nix and nil are not available on Windows")
@@ -250,3 +251,12 @@ class TestNixLanguageServer:
f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}",
pytrace=False,
)
@pytest.mark.parametrize("language_server", [Language.NIX], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.nix",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.ocaml
class TestOcamlDiagnostics:
@pytest.mark.parametrize("language_server", [Language.OCAML], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"lib/diagnostics_sample.ml",
(),
min_count=1,
)
@@ -0,0 +1,25 @@
import shutil
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.conftest import language_tests_enabled
from test.solidlsp.util.diagnostics import assert_file_diagnostics
pytestmark = [
pytest.mark.pascal,
pytest.mark.skipif(not language_tests_enabled(Language.PASCAL), reason="Pascal tests are disabled"),
pytest.mark.skipif(shutil.which("fpc") is None, reason="Pascal diagnostics require the Free Pascal compiler"),
]
class TestPascalDiagnostics:
@pytest.mark.parametrize("language_server", [Language.PASCAL], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.pas",
(),
min_count=1,
)
+10
View File
@@ -6,6 +6,7 @@ import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.perl
@@ -88,3 +89,12 @@ class TestPerlLanguageServer:
f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}",
pytrace=False,
)
@pytest.mark.parametrize("language_server", [Language.PERL], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.pl",
(),
min_count=1,
)
+17
View File
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.php
class TestPhpDiagnostics:
@pytest.mark.parametrize("language_server", [Language.PHP], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.php",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.powershell
class TestPowershellDiagnostics:
@pytest.mark.parametrize("language_server", [Language.POWERSHELL], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.ps1",
(),
min_count=1,
)
@@ -0,0 +1,19 @@
import os
import pytest
from solidlsp import SolidLanguageServer
from test.conftest import PYTHON_LANGUAGE_BACKENDS
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.python
class TestPythonDiagnostics:
@pytest.mark.parametrize("language_server", PYTHON_LANGUAGE_BACKENDS, indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
os.path.join("test_repo", "diagnostics_sample.py"),
("missing_user", "undefined_name"),
min_count=2,
)
+1 -1
View File
@@ -90,7 +90,7 @@ class TestRustLanguageServer:
all_symbols = request_all_symbols(language_server)
malformed_symbols = []
for s in all_symbols:
if has_malformed_name(s):
if has_malformed_name(s, whitespace_allowed=True):
malformed_symbols.append(s)
if malformed_symbols:
pytest.fail(
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.rust
class TestRustDiagnostics:
@pytest.mark.parametrize("language_server", [Language.RUST], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"src/diagnostics_sample.rs",
(),
min_count=1,
)
@@ -6,6 +6,7 @@ import pytest
from solidlsp.language_servers.scala_language_server import ScalaLanguageServer
from solidlsp.ls_config import Language, LanguageServerConfig
from solidlsp.settings import SolidLSPSettings
from test.solidlsp.util.diagnostics import assert_file_diagnostics
pytest.skip("Scala must be compiled for these tests to run through, which is a huge hassle", allow_module_level=True)
@@ -62,3 +63,12 @@ def test_scala_find_definition_and_references_across_files(scala_ls):
assert first_def["range"]["start"]["character"] == 6
assert first_def["range"]["end"]["line"] == 7
assert first_def["range"]["end"]["character"] == 14
def test_file_diagnostics(scala_ls) -> None:
assert_file_diagnostics(
scala_ls,
"src/main/scala/com/example/DiagnosticsSample.scala",
(),
min_count=1,
)
@@ -0,0 +1,24 @@
import sys
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.solidity
class TestSolidityDiagnostics:
@pytest.mark.xfail(
sys.platform == "darwin",
reason="Unknown — passes on Ubuntu but consistently fails on macOS CI; root cause not yet identified.",
strict=False,
)
@pytest.mark.parametrize("language_server", [Language.SOLIDITY], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"contracts/DiagnosticsSample.sol",
(),
min_count=1,
)
+10
View File
@@ -16,6 +16,7 @@ from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.conftest import is_ci
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics
# Skip Swift tests on Windows due to complex GitHub Actions configuration
WINDOWS_SKIP = platform.system() == "Windows"
@@ -238,3 +239,12 @@ class TestSwiftProjectBasics:
f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}",
pytrace=False,
)
@pytest.mark.parametrize("language_server", [Language.SWIFT], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"src/diagnostics_sample.swift",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.systemverilog
class TestSystemverilogDiagnostics:
@pytest.mark.parametrize("language_server", [Language.SYSTEMVERILOG], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.sv",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.terraform
class TestTerraformDiagnostics:
@pytest.mark.parametrize("language_server", [Language.TERRAFORM], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.tf",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.toml
class TestTomlDiagnostics:
@pytest.mark.parametrize("language_server", [Language.TOML], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.toml",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.typescript
class TestTypeScriptDiagnostics:
@pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.ts",
("missingGreeting", "missingConsumerValue"),
min_count=2,
)
+19
View File
@@ -0,0 +1,19 @@
from collections.abc import Sequence
from solidlsp import SolidLanguageServer
def assert_file_diagnostics(
language_server: SolidLanguageServer,
relative_file_path: str,
expected_message_fragments: Sequence[str],
min_count: int = 1,
) -> None:
diagnostics = language_server.request_text_document_diagnostics(relative_file_path, min_severity=1)
assert isinstance(diagnostics, list), diagnostics
assert len(diagnostics) >= min_count, diagnostics
diagnostic_messages = [diagnostic["message"] for diagnostic in diagnostics]
for fragment in expected_message_fragments:
assert any(fragment in message for message in diagnostic_messages), diagnostic_messages
+17
View File
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.vue
class TestVueDiagnostics:
@pytest.mark.parametrize("language_server", [Language.VUE], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"src/DiagnosticsSample.vue",
(),
min_count=1,
)
@@ -0,0 +1,17 @@
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.yaml
class TestYamlDiagnostics:
@pytest.mark.parametrize("language_server", [Language.YAML], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"diagnostics_sample.yaml",
(),
min_count=1,
)
+10
View File
@@ -14,6 +14,7 @@ from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from solidlsp.ls_types import SymbolKind
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics
@pytest.mark.zig
@@ -355,3 +356,12 @@ class TestZigLanguageServer:
f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}",
pytrace=False,
)
@pytest.mark.parametrize("language_server", [Language.ZIG], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"src/diagnostics_sample.zig",
(),
min_count=1,
)