Preserve newlines before tables/lists following bold/italic text
- Dominant language
- Python
- Stars
- 81
- Forks
- 11
- Avg merge
- 10h 53m
- Merged PRs (30d)
- 6
Description
## Problem
When a line ends with bold or italic text (like a label) followed by a newline, and the next line starts block content (a table or list), the parser may incorrectly merge them or the line wrapper may wrap them together. This is similar to the existing issue with Jinja/Markdoc tags that was addressed in `preprocess_tag_block_spacing()`.
### Examples that should preserve the newline
**Example 1: Table after bold label**
```markdown
**Ratings:**
| Source | Score | Votes |
| --- | --- | --- |
| IMDB | 85 | 12500 |
| Rotten Tomatoes | 92 | 450 |
```
**Example 2: List after bold label**
```markdown
**Items:**
1. First
2. Second
```
**Example 3: List after italic label**
```markdown
*Options:*
- Option A
- Option B
```
### Example that probably SHOULD wrap
```markdown
**Title:**
Test Movie
```
In this case, `Test Movie` is just plain text (not block content), so wrapping to `**Title:** Test Movie` is likely correct.
## Current Behavior
The existing `preprocess_tag_block_spacing()` function in `tag_handling.py` handles the case where:
- A **tag-only line** (like `{% field %}`) is followed by block content
- Block content is followed by a tag-only line
But it does NOT handle lines ending with bold/italic markers (`**`, `*`, `__`, `_`).
## Proposed Solution
There are a few approaches that could be combined:
### Approach 1: Extend block spacing preprocessing
Extend `preprocess_tag_block_spacing()` or add a similar function to detect:
- Lines ending with `**text:**` or `*text:*` patterns (bold/italic labels)
- When followed by block content (table row starting with `|` or list item starting with `-`, `*`, `+`, or `1.`)
- Insert a blank line to prevent merging
This would be similar to how tag-only lines are currently handled.
### Approach 2: Extend line wrapper newline preservation
Similar to how `add_tag_newline_handling()` preserves newlines around tags, add logic to preserve newlines when:
- Previous line ends with bold/italic markers (`**`, `*`)
- Current line is block content
### Approach 3: Improve sentence boundary detection for semantic line breaks
The sentence-end regex `SENTENCE_END_RE` in `sentence_split_regex.py` could be extended to recognize sentence-like boundaries across markdown formatting:
- `word.**` (bold ending)
- `word.*` (italic ending)
- `word:**` (colon at end of bold)
This would help semantic line breaking (`--semantic`) correctly place breaks after formatted labels.
### Recommended Approach
A combination of Approach 1 and 2 seems best:
1. Preprocess to add blank lines before block content when preceded by bold/italic labels
2. Extend the line wrapper's newline preservation to handle these cases
Approach 3 is a nice enhancement but addresses a slightly different use case (semantic breaking within paragraphs vs. preventing incorrect merging with block content).
## Implementation Notes
### Detection Heuristic
A line "ends with bold/italic" could be detected with a regex like:
```python
# Ends with closing bold/italic markers (possibly followed by punctuation)
line_ends_with_emphasis_re = re.compile(r'\*\*[^*]+\*\*[:.]?\s*$|\*[^*]+\*[:.]?\s*$')
```
Or more simply, check if the stripped line ends with `**` or `*` (but not `***` which is a thematic break).
### Block Content Detection
The existing `line_is_block_content()` in `block_heuristics.py` already handles this:
```python
def line_is_block_content(line: str) -> bool:
return line_is_table_row(line) or line_is_list_item(line)
```
## Test Cases to Add
Add these to `tests/testdocs/testdoc.orig.md`:
```markdown
### Block Content After Bold/Italic Labels
**Ratings:**
| Source | Score | Votes |
| --- | --- | --- |
| IMDB | 85 | 12500 |
| Rotten Tomatoes | 92 | 450 |
**Items:**
1. First
2. Second
*Options:*
- Option A
- Option B
**Title:**
Test Movie
```
The expected behavior:
- First three examples: newline should be preserved (block content follows)
- Last example: can be wrapped (plain text follows)
## Related Code
- `src/flowmark/linewrapping/tag_handling.py`: `preprocess_tag_block_spacing()`, `add_tag_newline_handling()`
- `src/flowmark/linewrapping/block_heuristics.py`: `line_is_block_content()`
- `src/flowmark/linewrapping/sentence_split_regex.py`: `SENTENCE_END_RE`
- `src/flowmark/linewrapping/line_wrappers.py`: `line_wrap_to_width()`, `line_wrap_by_sentence()`
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.