diff --git a/src/oca_pre_commit_hooks/checks_odoo_module_xml.py b/src/oca_pre_commit_hooks/checks_odoo_module_xml.py index dfe0cfa..426c501 100644 --- a/src/oca_pre_commit_hooks/checks_odoo_module_xml.py +++ b/src/oca_pre_commit_hooks/checks_odoo_module_xml.py @@ -686,7 +686,7 @@ def check_xml_double_quotes_py(self): message='Escaped double quotes " for python code detected', info=f"Use single quote instead: `{new_py_code}`", filepath=manifest_data["filename_short"], - line=elem.sourceline, + line=node_content.start_sourceline or elem.sourceline, ) during2 = node_content.content_node.replace(b""", b"'") if self.autofix and during2 != node_content.content_node: @@ -705,12 +705,20 @@ def check_xml_double_quotes_py(self): node_content = node_xml.NodeContent(manifest_data["filename"], elem) if b""" not in node_content.content_node: continue + + locator = self._get_tag_locator(manifest_data) + attr_span = locator.get_attr(elem, attr_name) + if attr_span: + line_no = locator.content[: attr_span.name_start].count(b"\n") + 1 + else: + line_no = node_content.start_sourceline or elem.sourceline + self.register_error( code="xml-double-quotes-py", message='Escaped double quotes " for python code detected use', info=f"Use single quote instead: `{new_py_code}`", filepath=manifest_data["filename_short"], - line=elem.sourceline, + line=line_no, ) during2 = node_content.content_node.replace(b""", b"'") if self.autofix and during2 != node_content.content_node: diff --git a/src/oca_pre_commit_hooks/node_xml.py b/src/oca_pre_commit_hooks/node_xml.py index 004cd69..07c57ef 100644 --- a/src/oca_pre_commit_hooks/node_xml.py +++ b/src/oca_pre_commit_hooks/node_xml.py @@ -1,4 +1,5 @@ # Based on https://github.com/mitsuhiko/sloppy-xml-py +import re from dataclasses import dataclass @@ -243,11 +244,11 @@ def _read_node(self): # noqa:C901 pylint:disable=too-complex # TODO: Get the sourceline of a particular attribute # Determine the search start line if (node_previous := self.node.getprevious()) is not None: - search_start_line = node_previous.sourceline + 1 + search_start_line = node_previous.sourceline elif (node_parent := self.node.getparent()) is not None: - search_start_line = node_parent.sourceline + 1 + search_start_line = node_parent.sourceline else: - search_start_line = 2 # first element and it is the root + search_start_line = 1 # first element and it is the root search_end_line = self.node.sourceline node_tag = self.node.tag.encode() if isinstance(self.node.tag, str) else self.node.tag @@ -257,14 +258,19 @@ def _read_node(self): # noqa:C901 pylint:disable=too-complex all_lines = list((i, line) for i, line in enumerate(f_content, start=1)) # Find the actual node start by looking for the tag + + node_start_re = re.compile(b"<" + node_tag + b"(?:[ />\n\r\t]|$)") node_start_idx = None - for idx, (no_line, line) in enumerate(all_lines): - if search_start_line <= no_line <= search_end_line: - stripped_line = line.lstrip() - if stripped_line.startswith(b"<" + node_tag): - node_start_idx = idx - self.start_sourceline = no_line - break + + end_idx = min(search_end_line - 1, len(all_lines) - 1) + start_idx = max(search_start_line - 1, 0) + + for idx in range(end_idx, start_idx - 1, -1): + no_line, line = all_lines[idx] + if node_start_re.search(line): + node_start_idx = idx + self.start_sourceline = no_line + break if node_start_idx is None: # Fallback: use search_end_line diff --git a/test_repo/test_module/model_view.xml b/test_repo/test_module/model_view.xml index 1fef119..b5179df 100644 --- a/test_repo/test_module/model_view.xml +++ b/test_repo/test_module/model_view.xml @@ -22,5 +22,23 @@ + + diff --git a/tests/test_checks.py b/tests/test_checks.py index 9b863ca..b4c5535 100644 --- a/tests/test_checks.py +++ b/tests/test_checks.py @@ -38,7 +38,7 @@ "xml-deprecated-qweb-directive-15": 4, "xml-deprecated-qweb-directive": 2, "xml-deprecated-tree-attribute": 3, - "xml-double-quotes-py": 3, + "xml-double-quotes-py": 5, "xml-duplicate-fields": 3, "xml-duplicate-record-id": 2, "xml-not-valid-char-link": 2,