Skip to content

Commit 552df6c

Browse files
author
Onimock
committed
Fix: QSSParser to strip selector comments and add full property assertions in test.
1 parent 3b103a9 commit 552df6c

File tree

2 files changed

+148
-12
lines changed

2 files changed

+148
-12
lines changed

src/qss_parser/qss_parser.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def __init__(self, selector: str) -> None:
275275
Args:
276276
selector (str): The CSS selector for this rule.
277277
"""
278-
self.selector: str = selector.strip()
278+
self.selector: str = SelectorUtils.strip_comments(selector).strip()
279279
self.properties: List[QSSProperty] = []
280280
self.object_name: Optional[str] = None
281281
self.class_name: Optional[str] = None
@@ -661,6 +661,23 @@ def validate_selector_syntax(selector: str, line_num: int) -> List[str]:
661661

662662
return errors
663663

664+
@staticmethod
665+
def strip_comments(line: str) -> str:
666+
"""
667+
Remove inline and block comments from a QSS line.
668+
669+
Args:
670+
line (str): The input line to process.
671+
672+
Returns:
673+
str: The line with comments removed.
674+
"""
675+
while "/*" in line and "*/" in line:
676+
start = line.index("/*")
677+
end = line.index("*/", start) + 2
678+
line = line[:start] + line[end:]
679+
return line.strip()
680+
664681

665682
class QSSFormatter:
666683
"""
@@ -983,7 +1000,7 @@ def process_line(
9831000
Returns:
9841001
bool: True if the line was processed by this plugin, False otherwise.
9851002
"""
986-
line = line.strip()
1003+
line = SelectorUtils.strip_comments(line)
9871004
if not line or state.in_comment or state.in_variables:
9881005
return False
9891006

@@ -1004,7 +1021,7 @@ def process_line(
10041021
if line.endswith("{") and not state.in_rule:
10051022
return self._start_rule(line, state, variable_manager)
10061023

1007-
if line == "}" and state.in_rule:
1024+
if line.strip() == "}" and state.in_rule:
10081025
return self._end_rule(state, variable_manager)
10091026

10101027
if line.endswith("{") and state.in_rule:
@@ -1034,7 +1051,7 @@ def _start_rule(
10341051
"""
10351052
state.buffer = ""
10361053
state.property_lines = []
1037-
selector_part = line[:-1].strip()
1054+
selector_part = SelectorUtils.strip_comments(line.split("{")[0].strip())
10381055
if selector_part:
10391056
normalized_selector = SelectorUtils.normalize_selector(selector_part)
10401057
selectors = [s.strip() for s in normalized_selector.split(",") if s.strip()]
@@ -1262,7 +1279,7 @@ def process_line(
12621279
Returns:
12631280
bool: True if the line was processed as a property, False otherwise.
12641281
"""
1265-
line = line.strip()
1282+
line = SelectorUtils.strip_comments(line).strip()
12661283
if not state.in_rule or state.in_comment or state.in_variables:
12671284
return False
12681285
if line.endswith("{") or line == "}":
@@ -1526,12 +1543,13 @@ def _process_line(self, line: str) -> None:
15261543
if not line:
15271544
return
15281545

1546+
clean_line = SelectorUtils.strip_comments(line)
15291547
if (
15301548
not self._state.in_rule
15311549
and not self._state.in_variables
15321550
and not self._state.in_comment
1533-
and ":" in line
1534-
and line.endswith(";")
1551+
and ":" in clean_line
1552+
and clean_line.endswith(";")
15351553
):
15361554
self.dispatch_error(
15371555
f"Error on line {self._state.current_line}: Property outside block: {line}"

tests/test_qss_parser.py

Lines changed: 123 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import sys
44
import unittest
5-
from typing import List, Set, Tuple
5+
from typing import Any, List, Set, Tuple
66
from unittest.mock import Mock
77

88
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")))
@@ -1317,7 +1317,7 @@ def test_get_style_for_multiple_selectors_separate_with_comma_in_each_line_with_
13171317
"""
13181318
qss: str = """
13191319
#myButton:hover,
1320-
QFrame:disabled,
1320+
QFrame:disabled,
13211321
#secondButton QPushButton,
13221322
#anyButton QCheckBox::drop-down:disabled {
13231323
color: red;
@@ -1777,7 +1777,7 @@ def test_to_string_comments_bigger_than_a_line(self) -> None:
17771777
height: 15px;
17781778
border-radius: 10px;
17791779
}
1780-
/* Another comment
1780+
/* Another comment
17811781
more comment*/
17821782
#customButton {
17831783
qproperty-enabled: false;
@@ -1809,6 +1809,124 @@ def test_to_string_comments_bigger_than_a_line(self) -> None:
18091809
)
18101810
self.assertEqual(self.errors, [], "Comments-only QSS should produce no errors")
18111811

1812+
def test_to_string_comments_in_single_line_inside_selector(self) -> None:
1813+
"""
1814+
Test parsing QSS with comments in single and multi-line formats inside and outside selectors,
1815+
ensuring rules are correctly parsed and comments are excluded from output.
1816+
"""
1817+
self.maxDiff = None
1818+
qss: str = """
1819+
/* This is valid but not include on parsing */
1820+
/* Another comment
1821+
This is valid but not include on parsing
1822+
*/
1823+
#secondButton QPushButton { /* This is valid but not include on parsing */
1824+
color: red;
1825+
width: 15px; /*This is valid but not include on parsing */
1826+
height: 15px;
1827+
border-radius: 10px;
1828+
/*comment here */
1829+
}
1830+
/*This is valid but not include on parsing*/
1831+
#customButton {
1832+
qproperty-enabled: false;
1833+
background: gray;
1834+
} /*This is valid but not include on parsing*/
1835+
/*more comment */
1836+
"""
1837+
expected = """#secondButton QPushButton {
1838+
color: red;
1839+
width: 15px;
1840+
height: 15px;
1841+
border-radius: 10px;
1842+
}
1843+
1844+
#customButton {
1845+
qproperty-enabled: false;
1846+
background: gray;
1847+
}
1848+
"""
1849+
self.parser.parse(qss)
1850+
self.assertEqual(
1851+
len(self.parser._state.rules),
1852+
2,
1853+
"QSS with comments should result in two rules",
1854+
)
1855+
rules_1 = self.parser._state.rules[0]
1856+
rules_2 = self.parser._state.rules[1]
1857+
self.assertEqual(
1858+
rules_1.class_name,
1859+
"QPushButton",
1860+
"Expected className for first rule to be QPushButton",
1861+
)
1862+
self.assertEqual(
1863+
rules_1.object_name,
1864+
"secondButton",
1865+
"Expected objectName for first rule to be secondButton",
1866+
)
1867+
self.assertEqual(
1868+
rules_2.class_name,
1869+
None,
1870+
"Expected className for second rule to be None",
1871+
)
1872+
self.assertEqual(
1873+
rules_2.object_name,
1874+
"customButton",
1875+
"Expected objectName for second rule to be customButton",
1876+
)
1877+
self.assertEqual(
1878+
len(rules_1.properties),
1879+
4,
1880+
"Expected 4 properties for first rule",
1881+
)
1882+
self.assertEqual(
1883+
len(rules_2.properties),
1884+
2,
1885+
"Expected 2 properties for second rule",
1886+
)
1887+
# Assertions for all properties of first rule
1888+
self.assertEqual(
1889+
f"{rules_1.properties[0].name}: {rules_1.properties[0].value}",
1890+
"color: red",
1891+
"Expected first property of first rule to be 'color: red'",
1892+
)
1893+
self.assertEqual(
1894+
f"{rules_1.properties[1].name}: {rules_1.properties[1].value}",
1895+
"width: 15px",
1896+
"Expected second property of first rule to be 'width: 15px'",
1897+
)
1898+
self.assertEqual(
1899+
f"{rules_1.properties[2].name}: {rules_1.properties[2].value}",
1900+
"height: 15px",
1901+
"Expected third property of first rule to be 'height: 15px'",
1902+
)
1903+
self.assertEqual(
1904+
f"{rules_1.properties[3].name}: {rules_1.properties[3].value}",
1905+
"border-radius: 10px",
1906+
"Expected fourth property of first rule to be 'border-radius: 10px'",
1907+
)
1908+
# Assertions for all properties of second rule
1909+
self.assertEqual(
1910+
f"{rules_2.properties[0].name}: {rules_2.properties[0].value}",
1911+
"qproperty-enabled: false",
1912+
"Expected first property of second rule to be 'qproperty-enabled: false'",
1913+
)
1914+
self.assertEqual(
1915+
f"{rules_2.properties[1].name}: {rules_2.properties[1].value}",
1916+
"background: gray",
1917+
"Expected second property of second rule to be 'background: gray'",
1918+
)
1919+
self.assertEqual(
1920+
self.parser.to_string(),
1921+
expected,
1922+
"Expected formatted QSS string to match",
1923+
)
1924+
self.assertEqual(
1925+
self.errors,
1926+
[],
1927+
"QSS with comments should produce no errors",
1928+
)
1929+
18121930
def test_to_string_comments_bigger_than_a_line_and_single_line(self) -> None:
18131931
"""
18141932
Test parsing QSS with only comments in bigger than a line and in a single line.
@@ -1826,7 +1944,7 @@ def test_to_string_comments_bigger_than_a_line_and_single_line(self) -> None:
18261944
border-radius: 10px;
18271945
}
18281946
/*More single-line comment*/
1829-
/* Another comment
1947+
/* Another comment
18301948
more comment*/
18311949
#customButton {
18321950
qproperty-enabled: false;
@@ -2030,7 +2148,7 @@ def test_event_qproperty_defined(self) -> None:
20302148
self.assertEqual(
20312149
len(rules_added), 3, "Should trigger rule_added for each qproperty rule"
20322150
)
2033-
selectors: set = {rule.selector for rule in rules_added}
2151+
selectors: Set[Any] = {rule.selector for rule in rules_added}
20342152
self.assertEqual(
20352153
selectors,
20362154
{

0 commit comments

Comments
 (0)