Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions site/src/pages/formatter.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ regardless of the `normalizeSpacing` setting.
- **Indented lines** — any line starting with spaces or tabs is left as-is
- **Tab-containing lines** — command references like `CTRL-V\t\tDescription` are
preserved and not merged with adjacent prose
- **Pipe tables** — lines that start and end with `|` are preserved verbatim,
covering both padded (`| cell | cell |`) and tight (`|cell|cell|`) GFM-style
tables
- **Blank lines** — structure-preserving

## Limitations
Expand Down
22 changes: 22 additions & 0 deletions site/src/pages/grammar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,28 @@ Content between backticks is skipped during tag scanning.

A `|pipe|` pattern inside a backtick span is not recognized as a taglink.

## Pipe Tables

Lines that start and end with `|` (after trimming trailing whitespace) are
recognized as pipe table rows and preserved verbatim by the formatter. This
covers both padded and tight GFM-style markdown tables.

```
| Command | List |
| -------- | -------------- |
| `files` | find or fd |
```

```
|Prefix |Behavior |
|-----------|---------------|
|`no prefix`|Files |
```

Pipe table rows are not reflowed, not merged with adjacent prose, and not
treated as taglinks. Traditional vimdoc tables that use tab-aligned columns
with `~` headers are already preserved by the tab-containing-line rule.

## Blank Lines

An empty line or a line containing only whitespace is classified as `Blank`.
Expand Down
72 changes: 71 additions & 1 deletion src/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub fn format_document(text: &str, opts: &FormatOptions) -> String {
if pl.tag_defs.is_empty() {
let indent = leading_whitespace(raw_lines[i]);
if indent.is_empty() {
if raw_lines[i].contains('\t') {
if raw_lines[i].contains('\t') || is_pipe_table_row(raw_lines[i]) {
out.push(raw_lines[i].trim_end().to_string());
i += 1;
} else {
Expand Down Expand Up @@ -115,6 +115,7 @@ fn emit_prose_paragraph(
&& doc.lines[j].tag_defs.is_empty()
&& leading_whitespace(raw_lines[j]).is_empty()
&& !raw_lines[j].contains('\t')
&& !is_pipe_table_row(raw_lines[j])
{
j += 1;
}
Expand Down Expand Up @@ -189,6 +190,11 @@ fn leading_whitespace(s: &str) -> &str {
&s[..s.len() - trimmed.len()]
}

fn is_pipe_table_row(s: &str) -> bool {
let trimmed = s.trim_end();
trimmed.starts_with('|') && trimmed.len() > 1 && trimmed.ends_with('|')
}

fn split_words_with_spacing(s: &str) -> Vec<(&str, usize)> {
let bytes = s.as_bytes();
let mut result = Vec::new();
Expand Down Expand Up @@ -440,6 +446,70 @@ mod tests {
assert!(result.lines().all(|l| l.len() <= 78));
}

#[test]
fn pipe_table_padded_preserved() {
let input = "\
| Command | List |
| -------- | -------------- |
| `files` | find or fd |
| `buffers` | open buffers |
";
let result = format_document(input, &FormatOptions::default());
assert_eq!(result, input);
}

#[test]
fn pipe_table_tight_preserved() {
let input = "\
|Prefix |Behavior |
|-----------|-----------------------------------|
|`no prefix`|Files |
|`$` |Buffers |
";
let result = format_document(input, &FormatOptions::default());
assert_eq!(result, input);
}

#[test]
fn pipe_table_not_merged_with_adjacent_prose() {
let input = "\
Prose before the table.

| Command | List |
| -------- | ---------- |
| `files` | find or fd |

Prose after the table.
";
let result = format_document(input, &FormatOptions::default());
assert_eq!(result, input);
}

#[test]
fn pipe_table_idempotent() {
let input = "\
| Key | Command | Key | Command |
| ----------| ------------------| ----------| ------------------|
| `<C-\\>` | buffers | `<C-p>` | files |
";
let once = format_document(input, &FormatOptions::default());
let twice = format_document(&once, &FormatOptions::default());
assert_eq!(once, twice);
}

#[test]
fn pipe_table_prose_after_not_blocked() {
let input = "\
| Col | Val |

word1 word2
word3 word4
";
let result = format_document(input, &FormatOptions::default());
assert!(result.contains("| Col | Val |"));
assert!(result.contains("word1 word2 word3 word4"));
}

#[test]
fn normalize_spacing_collapses_double_space() {
let input = "First sentence. Second sentence.\n";
Expand Down
Loading