Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: format most commented elements #161

Merged
merged 9 commits into from
Nov 29, 2024
Merged
68 changes: 39 additions & 29 deletions docs/limitations.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,70 @@
To keep source code valid, typstyle will give up formatting in certain cases. This is a list of what typstyle will not format.
To ensure source code remains valid, typstyle will refrain from formatting in certain scenarios. Below is a list of cases where typstyle does not apply formatting.

## Overall

### Markup lines
### `@typstyle off`

Typstyle only formats the code, it does not format the markup lines. It will keep the markup lines as is. Specifically, if a line contains text(`ast::Expr::Text`), the whole line will be kept as is.
Why: This directive explicitly disables formatting.

### Math mode
### Markup Lines

Overall there is very few formatting in math mode. It is not well implemented.
Typstyle only formats code and does not alter markup lines. These lines are preserved as-is. Specifically, if a line contains text (`ast::Expr::Text`), the entire line will remain unformatted.

### @typstyle off
### Math Mode

Why: It is a directive to turn off formatting.
Formatting in math mode is minimal and not well-implemented at this time.

### When there are block comment child, gives up formatting the whole node.
### Expressions with Comments

Why: We currently cannot handle block comment in all places. It is hard to handle.
In the following scenarios, when a block comment is present as a child, the entire node is skipped for formatting:

```typst
#let f(a, /* they */ b) = if /* are */ a > b {
a
} /* everywhere */ else {
b
}
```
- Parenthesized (e.g., `((a))`)
- Field access (e.g., `a.b.c`)
- Function call (e.g., `f(a, b, c)`)

Why: These cases require special handling to bring better reading experience. Interspersing comments within them introduces additional complexity that is not yet resolved.

We guarantee that in all formatable cases, no comments should be lost.
If any comments are lost, please submit a PR to present the issue.

### Multiline raw with single backtick

Why: These strings are whitespace-dependent.

### Multiline raw with single backtick.
Why: it is white space dependent
```typst
`a
b`
is not
`a
b`
```
### When a child contains # in math mode, gives up formatting the whole node.
Why: hash can appear anywhere, we cannot handle it very well.

### Nodes with `#` in Math Mode

If a child node contains `#` in math mode, typstyle will skip formatting the entire node.

Why: Hashes can appear anywhere, and handling them accurately is challenging.

```typst
$f(a+b, size: #1em)$
```
### Args in math mode.
Why: it works very different. like 2d args, trailing commas

### Args in Math Mode

Why: Arguments in math mode behave differently, such as 2D arguments and trailing commas.

```typst
$mat(a,,;,b,;,,c)$
```
## Special

## Special Cases

### Table

Typstyle currently tries to format tables into a rectangular shape. However, it only do such formatting when the table is simple enough. Namely:
Typstyle attempts to format tables into a rectangular shape, but only when the table is simple enough. A table is considered "simple" if it meets the following conditions:

1. no comments
2. no spread args
3. no named args, or named args appears before all pos args
4. no `table/grid.vline/hline/cell`
5. `columns` is int or array
1. No comments.
2. No spread args.
3. No named args, or named args appears before all pos args.
4. No `table/grid.vline/hline/cell`.
5. `columns` is int or array.
78 changes: 40 additions & 38 deletions src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,12 @@ struct State {

#[derive(Debug, Clone, Default)]
pub struct Attributes {
no_format: bool,
/// Manually marked `@typstyle off` or always ignored.
pub format_disabled: bool,
/// Has a child of comment.
pub commented: bool,
/// If (a) the first space child contains a newline, (b) one of the children is a multiline
multiline: bool,
}

impl Attributes {
pub fn no_format(&self) -> bool {
self.no_format
}

pub fn is_multiline_flavor(&self) -> bool {
self.multiline
}
pub multiline: bool,
}

#[derive(Debug, Default)]
Expand All @@ -41,16 +34,28 @@ impl AttrStore {
store
}

pub fn is_node_commented(&self, node: &SyntaxNode) -> bool {
self.attr_map
.get(&node.span())
.is_some_and(|attr| attr.commented)
}

pub fn is_node_multiline(&self, node: &SyntaxNode) -> bool {
self.attr_map
.get(&node.span())
.is_some_and(|attr| attr.multiline)
}

pub fn is_node_no_format(&self, node: &SyntaxNode) -> bool {
pub fn is_node_format_disabled(&self, node: &SyntaxNode) -> bool {
self.attr_map
.get(&node.span())
.is_some_and(|attr| attr.no_format)
.is_some_and(|attr| attr.format_disabled)
}

pub fn is_node_unformattable(&self, node: &SyntaxNode) -> bool {
self.attr_map
.get(&node.span())
.is_some_and(|attr| attr.format_disabled || attr.commented)
}

fn compute_multiline(&mut self, root: &SyntaxNode) {
Expand Down Expand Up @@ -87,10 +92,10 @@ impl AttrStore {
}

fn compute_no_format_impl(&mut self, node: &SyntaxNode, state: &mut State) {
if self.is_node_no_format(node) {
if self.is_node_format_disabled(node) {
return;
}
let mut no_format = false;
let mut format_disabled = false;
let original_is_math = state.is_math;
if node.is::<Math>() {
state.is_math = true;
Expand All @@ -99,56 +104,53 @@ impl AttrStore {
for child in node.children() {
let child_kind = child.kind();
if child_kind == SyntaxKind::LineComment || child_kind == SyntaxKind::BlockComment {
self.set_commented(node);
// @typstyle off affects the whole next block
if child.text().contains("@typstyle off") {
no_format = true;
self.set_no_format(child);
}
// currently we only support comments as children of these nodes
if !matches!(
node.kind(),
SyntaxKind::ContentBlock
| SyntaxKind::CodeBlock
| SyntaxKind::Code
| SyntaxKind::Array
| SyntaxKind::Dict
) {
self.set_no_format(node);
format_disabled = true;
self.set_format_disabled(child);
}
continue;
}
// no format multiline single backtick raw block
if let Some(raw) = child.cast::<Raw>() {
if !raw.block() && raw.lines().count() > 1 {
self.set_no_format(child);
self.set_format_disabled(child);
}
}
// no format hash related nodes in math blocks
if child_kind == SyntaxKind::Hash && state.is_math {
self.set_no_format(node);
self.set_format_disabled(node);
break;
}
// no format args in math blocks
if child.cast::<Args>().is_some() && state.is_math {
self.set_no_format(child);
if child.is::<Args>() && state.is_math {
self.set_format_disabled(child);
continue;
}
if child.children().count() == 0 {
continue;
}
// no format nodes with @typstyle off
if no_format {
self.set_no_format(child);
no_format = false;
if format_disabled {
self.set_format_disabled(child);
format_disabled = false;
continue;
}
self.compute_no_format_impl(child, state);
}
state.is_math = original_is_math;
}

fn set_no_format(&mut self, node: &SyntaxNode) {
self.attr_map.entry(node.span()).or_default().no_format = true;
fn set_format_disabled(&mut self, node: &SyntaxNode) {
self.attr_map
.entry(node.span())
.or_default()
.format_disabled = true;
}

fn set_commented(&mut self, node: &SyntaxNode) {
self.attr_map.entry(node.span()).or_default().commented = true;
}
}

Expand Down
117 changes: 0 additions & 117 deletions src/pretty/arg.rs

This file was deleted.

Loading
Loading