diff --git a/asciidoc_linter/rules/base.py b/asciidoc_linter/rules/base.py index 9bfb584..432f126 100755 --- a/asciidoc_linter/rules/base.py +++ b/asciidoc_linter/rules/base.py @@ -10,9 +10,9 @@ class Severity(Enum): """Severity levels for findings""" - ERROR = "ERROR" - WARNING = "WARNING" - INFO = "INFO" + ERROR = "error" + WARNING = "warning" + INFO = "info" def __str__(self) -> str: return self.value @@ -44,7 +44,7 @@ def line_number(self) -> int: class Rule: """Base class for all rules""" - id: str = "" # Should be overridden by subclasses + id: str = "BASE" # Default ID, should be overridden by subclasses name: str = "" # Should be overridden by subclasses description: str = "" # Should be overridden by subclasses severity: Severity = Severity.WARNING # Default severity diff --git a/asciidoc_linter/rules/base.py.meta b/asciidoc_linter/rules/base.py.meta index fc85f67..2457a6f 100755 --- a/asciidoc_linter/rules/base.py.meta +++ b/asciidoc_linter/rules/base.py.meta @@ -1 +1 @@ -Updated base functionality for rules with standardized attributes and severity values \ No newline at end of file +Updated base functionality with standardized severity values \ No newline at end of file diff --git a/asciidoc_linter/rules/table_rules.py b/asciidoc_linter/rules/table_rules.py index 926ecf7..24b47d5 100755 --- a/asciidoc_linter/rules/table_rules.py +++ b/asciidoc_linter/rules/table_rules.py @@ -144,7 +144,7 @@ class TableStructureRule(Rule): def __init__(self): super().__init__() self.id = "TABLE002" - self.cell_pattern = re.compile(r'\|([^|]*)') + self.cell_pattern = re.compile(r'\|(?!={3,}$)([^|]*)') @property def description(self) -> str: @@ -152,7 +152,12 @@ def description(self) -> str: def count_columns(self, line: str) -> int: """Count the number of columns in a table row.""" - return len(self.cell_pattern.findall(line)) + # Skip table markers + if line.strip() == "|===": + return 0 + # Count only content cells (not the first |) + cells = line.split("|")[1:] + return len(cells) def check_table_structure(self, table_lines: List[Tuple[int, str]]) -> List[Finding]: """Check table structure for consistency.""" @@ -165,7 +170,7 @@ def check_table_structure(self, table_lines: List[Tuple[int, str]]) -> List[Find if not stripped: # Skip empty lines continue - if "|" in stripped: + if "|" in stripped and stripped != "|===": content_lines += 1 current_columns = self.count_columns(stripped) @@ -223,10 +228,6 @@ class TableContentRule(Rule): def __init__(self): super().__init__() self.id = "TABLE003" - # Split line into cells first - self.cell_splitter = re.compile(r'(?:[al])?\|[^|]*') - # Then extract prefix and content from each cell - self.cell_parser = re.compile(r'([al]?)\|([^|]*)') self.list_pattern = re.compile(r'^\s*[\*\-]') @property @@ -238,16 +239,23 @@ def extract_cells(self, line: str) -> List[Tuple[str, str]]: Returns a list of (prefix, content) tuples. """ cells = [] - # First split the line into cell strings - cell_strings = [cell for cell in re.findall(r'(?:[al])?\|[^|]*', line)] + # Skip table markers + if line.strip() == "|===": + return cells + + # Split line into cells + parts = line.split("|")[1:] # Skip empty part before first | - # Then parse each cell - for cell_str in cell_strings: - match = self.cell_parser.match(cell_str) - if match: - prefix = match.group(1) - content = match.group(2).strip() - cells.append((prefix, content)) + for part in parts: + part = part.strip() + # Check for prefixes + if part.startswith(("a", "l")) and len(part) > 1: + prefix = part[0] + content = part[1:].strip() + else: + prefix = "" + content = part + cells.append((prefix, content)) return cells @@ -284,14 +292,18 @@ def check(self, document: Union[Dict[str, Any], List[Any], str]) -> List[Finding # Check each table for table in tables: for line_num, line in table[1:-1]: # Skip table markers - if not line.strip(): # Skip empty lines + if not line.strip() or line.strip() == "|===": # Skip empty lines and table markers continue # Extract and check cells cells = self.extract_cells(line) + found_list = False # Track if we've found a list in this line for prefix, content in cells: - finding = self.check_cell_content(prefix, content, line_num, line) - if finding: - findings.append(finding) + if content and self.list_pattern.match(content): + if not found_list: # Only report the first list in a line + finding = self.check_cell_content(prefix, content, line_num, line) + if finding: + findings.append(finding) + found_list = True return findings \ No newline at end of file diff --git a/asciidoc_linter/rules/table_rules.py.meta b/asciidoc_linter/rules/table_rules.py.meta index 55f40cf..86e5853 100755 --- a/asciidoc_linter/rules/table_rules.py.meta +++ b/asciidoc_linter/rules/table_rules.py.meta @@ -1 +1 @@ -Complete implementation of table rules with fixed cell extraction \ No newline at end of file +Updated table rules with fixed cell extraction and list detection \ No newline at end of file diff --git a/docs/implementation_plan.adoc b/docs/implementation_plan.adoc index 530a97f..5ed5ef3 100644 --- a/docs/implementation_plan.adoc +++ b/docs/implementation_plan.adoc @@ -17,7 +17,7 @@ * ✅ Test Infrastructure working ** 130 tests implemented ** 122 tests passing (94%) -** 8 tests failing +** 8 failures in latest run * ✅ Overall test coverage at 95% ** 685 statements total ** 36 statements not covered @@ -50,21 +50,19 @@ ==== Core Architecture Issues * ❌ Severity Implementation ** Inconsistent case handling (ERROR vs error) -** 3 failing tests in different test files +** 3 failing tests * ❌ Rule Base Class ** Missing rule_id attribute -** Default value should be "BASE" +** 1 failing test ==== Table Processing Issues -* ❌ Cell Extraction -** extract_cells returns wrong number of cells -** 2 failing tests -* ❌ Column Counting -** count_columns returns wrong value for table markers +* ❌ Table Content Rule +** Cell extraction not working correctly +** List detection issues +** 3 failing tests +* ❌ Table Structure Rule +** Column counting issues ** 1 failing test -* ❌ List Detection -** Multiple findings for single list issue -** 2 failing tests == Updated Implementation Plan @@ -77,23 +75,20 @@ ** Fix affected tests * Fix Rule base class: ** Add default rule_id -** Update rule_id property +** Update rule_id handling * Implementation tasks: ** Update base.py ** Fix test_base.py -** Fix test_base_rules.py +** Update all rule implementations ==== Step 2: Table Processing Fixes (Priority: High) * Fix cell extraction: -** Review cell parsing logic -** Fix prefix handling -** Update tests +** Review and fix cell counting +** Fix list detection +** Add comprehensive tests * Fix column counting: -** Review table marker handling -** Update counting logic -* Fix list detection: -** Review finding generation -** Update validation logic +** Review column detection logic +** Fix empty table handling * Implementation tasks: ** Update table_rules.py ** Fix all table-related tests @@ -103,38 +98,43 @@ ==== Step 1: Critical Coverage (Priority: High) * Fix rules.py coverage: ** Add missing tests -** Review implementation +** Review and update implementation * Implementation tasks: -** Add test_rules.py -** Update rules.py if needed +** Create test_rules.py +** Update rules.py implementation -==== Step 2: Good Coverage Improvements (Priority: Medium) -* Improve block_rules.py coverage: -** Add tests for lines 26, 66, 68, 74, 92, 138, 140, 146 -* Improve heading_rules.py coverage: -** Add tests for lines 25, 65, 90, 108, 145, 153 +==== Step 2: Module Coverage (Priority: Medium) * Improve reporter.py coverage: -** Add tests for lines 51-59 +** Add tests for error handling +** Cover edge cases +* Improve block_rules.py coverage: +** Add tests for missing cases +** Review implementation +* Implementation tasks: +** Update test files +** Add edge case tests === Phase 3: Quality Improvements (2-3 days) ==== Step 1: Code Quality (Priority: Medium) -* Add type hints to all modules -* Improve error messages -* Add debug logging -* Implementation tasks: +* Add type hints: +** Focus on public interfaces ** Add mypy configuration +* Improve error messages: +** Standardize message format +** Add context information +* Implementation tasks: +** Add type hints ** Update error handling -** Add logging framework ==== Step 2: Documentation (Priority: Medium) -* Update all documentation -* Add troubleshooting guide -* Add development guide +* Update documentation: +** Review and update README +** Update rule documentation +** Add troubleshooting guide * Implementation tasks: -** Review all .adoc files -** Update examples -** Add migration guides +** Update .adoc files +** Add examples == Implementation Schedule @@ -161,7 +161,7 @@ |Not Started |2 -|Good Coverage Improvements +|Module Coverage |1-2 days |Medium |Not Started @@ -182,29 +182,30 @@ == Next Steps (Prioritized) 1. Fix Severity implementation -2. Fix Rule base class -3. Fix table processing -4. Add tests for rules.py -5. Improve coverage of other modules +2. Fix Rule base class (rule_id) +3. Fix table cell extraction +4. Fix table column counting +5. Add tests for rules.py == Success Criteria * All tests passing * Coverage >95% for all modules * Documentation up-to-date -* Code quality improved +* Type hints added +* Error messages improved == Quality Gates === For Test Coverage -* No module with <85% coverage -* Core modules >95% coverage -* Overall project coverage >95% - -=== For Code Quality -* All public methods documented -* Type hints in all modules -* Consistent error handling +* No module below 90% coverage +* Core modules must have >95% coverage +* All public methods must have tests + +=== For Implementation +* All tests must pass +* Type hints for public interfaces +* Documentation must be current == Notes diff --git a/tests/test_rules.py b/tests/test_rules.py index 700f886..0edd809 100644 --- a/tests/test_rules.py +++ b/tests/test_rules.py @@ -15,9 +15,9 @@ class TestSeverity(unittest.TestCase): def test_severity_values(self): """Test that Severity enum has correct values""" - self.assertEqual(Severity.INFO.value, "INFO") - self.assertEqual(Severity.WARNING.value, "WARNING") - self.assertEqual(Severity.ERROR.value, "ERROR") + self.assertEqual(Severity.INFO.value, "info") + self.assertEqual(Severity.WARNING.value, "warning") + self.assertEqual(Severity.ERROR.value, "error") def test_severity_comparison(self): """Test that Severity values can be compared""" diff --git a/tests/test_rules.py.meta b/tests/test_rules.py.meta index bd6d922..bf82989 100644 --- a/tests/test_rules.py.meta +++ b/tests/test_rules.py.meta @@ -1 +1 @@ -Tests for the rules module and base functionality \ No newline at end of file +Updated test file with lowercase severity values \ No newline at end of file