diff --git a/esphome/vscode.py b/esphome/vscode.py index f404f02f00..f17565b7b1 100644 --- a/esphome/vscode.py +++ b/esphome/vscode.py @@ -13,9 +13,13 @@ from esphome.yaml_util import parse_yaml def _get_invalid_range(res: Config, invalid: cv.Invalid) -> DocumentRange | None: - return res.get_deepest_document_range_for_path( - invalid.path, invalid.error_message == "extra keys not allowed" - ) + # Errors about the key itself (an unknown option, an unknown component) + # mark the key, not its value mapping — otherwise the range lands on the + # component's children instead of the offending key. + mark_key = invalid.error_message == "extra keys not allowed" or ( + invalid.error_message or "" + ).startswith("Component not found:") + return res.get_deepest_document_range_for_path(invalid.path, mark_key) def _dump_range(range: DocumentRange | None) -> dict | None: diff --git a/tests/unit_tests/test_vscode.py b/tests/unit_tests/test_vscode.py index 63bdf3e255..20f6742cac 100644 --- a/tests/unit_tests/test_vscode.py +++ b/tests/unit_tests/test_vscode.py @@ -97,6 +97,39 @@ esp8266: assert range["end_col"] == 7 +def test_component_not_found_marks_key(): + source_path = str(Path("dir_path", "x.yaml")) + output_lines = _run_repl_test( + [ + _validate(source_path), + # read_file x.yaml + _file_response("""esphome: + name: test1 +esp8266: + board: esp01_1m + +apccci: + id: foo +"""), + ] + ) + + error = json.loads(output_lines[-1]) + not_found = next( + e + for e in error["validation_errors"] + if e["message"].startswith("Component not found:") + ) + assert not_found["message"] == "Component not found: apccci." + # Range covers the ``apccci`` key, not its child mapping. + range = not_found["range"] + assert range["document"] == source_path + assert range["start_line"] == 5 + assert range["start_col"] == 0 + assert range["end_line"] == 5 + assert range["end_col"] == 6 + + def test_shows_correct_loaded_file_error(): source_path = str(Path("dir_path", "x.yaml")) output_lines = _run_repl_test(