master 61dc16d002ba cached
202 files
764.2 KB
246.1k tokens
1506 symbols
1 requests
Download .txt
Showing preview only (821K chars total). Download the full file or copy to clipboard to get everything.
Repository: jinxdash/prettier-plugin-rust
Branch: master
Commit: 61dc16d002ba
Files: 202
Total size: 764.2 KB

Directory structure:
gitextract_sexpc5gs/

├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── Bug_report.yml
│       └── config.yml
├── .gitignore
├── .gitmodules
├── .npmrc
├── .vscode/
│   ├── launch.json
│   └── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── crate/
│   ├── Cargo.toml
│   ├── README.md
│   └── src/
│       └── main.rs
├── extension/
│   ├── LICENSE
│   ├── README.md
│   ├── package.json
│   ├── src/
│   │   └── index.ts
│   └── tsconfig.json
├── package.json
├── pnpm-workspace.yaml
├── scripts/
│   ├── build.ts
│   ├── dev.format.ts
│   ├── dev.repl.ts
│   └── tsconfig.json
├── src/
│   ├── format/
│   │   ├── comments.ts
│   │   ├── complexity.ts
│   │   ├── core.ts
│   │   ├── external.ts
│   │   ├── plugin.ts
│   │   ├── printer.ts
│   │   └── styling.ts
│   ├── index.ts
│   ├── transform/
│   │   ├── custom/
│   │   │   ├── attribute.ts
│   │   │   ├── cfg_if.ts
│   │   │   └── utils.ts
│   │   └── index.ts
│   └── utils/
│       ├── common.ts
│       └── debug.ts
├── tests/
│   ├── output/
│   │   ├── comments/
│   │   │   ├── assignment.f.rs
│   │   │   ├── binaryish.f.rs
│   │   │   ├── blocks.f.rs
│   │   │   ├── chain.f.rs
│   │   │   ├── closure.f.rs
│   │   │   ├── dangling.f.rs
│   │   │   ├── file.f.rs
│   │   │   ├── flow.f.rs
│   │   │   ├── functions.f.rs
│   │   │   ├── ignore.attr.f.rs
│   │   │   ├── ignore.f.rs
│   │   │   ├── ignore.file.f.rs
│   │   │   ├── imports.f.rs
│   │   │   ├── macro.f.rs
│   │   │   ├── multiple.f.rs
│   │   │   ├── parens.f.rs
│   │   │   └── whitespace.f.rs
│   │   ├── common/
│   │   │   ├── arrays.f.rs
│   │   │   ├── assignments.f.rs
│   │   │   ├── binaryish.f.rs
│   │   │   ├── chains.f.rs
│   │   │   ├── chains.first-argument-expansion.f.rs
│   │   │   ├── chains.last-argument-expansion.f.rs
│   │   │   ├── closures.f.rs
│   │   │   ├── destructuring.f.rs
│   │   │   ├── members.f.rs
│   │   │   └── types.f.rs
│   │   ├── issues/
│   │   │   ├── 0.f.rs
│   │   │   ├── 14.f.rs
│   │   │   ├── 21/
│   │   │   │   ├── fn_comment.f.rs
│   │   │   │   ├── fn_fn.f.rs
│   │   │   │   ├── fn_ln.f.rs
│   │   │   │   ├── ln_fn_ln.f.rs
│   │   │   │   └── mod.f.rs
│   │   │   ├── 22.f.rs
│   │   │   ├── 25.f.rs
│   │   │   ├── nth-pass.f.1.rs
│   │   │   └── nth-pass.f.rs
│   │   ├── macros/
│   │   │   ├── cfg_if.f.rs
│   │   │   ├── if_chain.f.rs
│   │   │   └── matches.f.rs
│   │   └── styling/
│   │       ├── blockify.f.rs
│   │       ├── canInlineBlockBody.f.rs
│   │       ├── needsParens.f.rs
│   │       └── needsSemi.f.rs
│   ├── output-ext/
│   │   ├── errors/
│   │   │   └── foo.rs
│   │   ├── expressions/
│   │   │   ├── block.f.rs
│   │   │   ├── closure.f.rs
│   │   │   ├── expr.f.rs
│   │   │   ├── flow_expr.f.rs
│   │   │   ├── ident.f.rs
│   │   │   ├── literal.f.rs
│   │   │   ├── match.f.rs
│   │   │   ├── parens.f.rs
│   │   │   ├── precedence.f.rs
│   │   │   └── range.f.rs
│   │   ├── features/
│   │   │   ├── arbitrary_enum_discriminant.f.rs
│   │   │   ├── associated_type_bounds.f.rs
│   │   │   ├── async_closure.f.rs
│   │   │   ├── auto_traits.f.rs
│   │   │   ├── const_generics_defaults.f.rs
│   │   │   ├── const_trait_impl.f.rs
│   │   │   ├── decl_macro.f.rs
│   │   │   ├── destructuring_assignment.f.rs
│   │   │   ├── generators.f.rs
│   │   │   ├── if_let_guard.f.rs
│   │   │   ├── inline_const.f.rs
│   │   │   ├── inline_const_pat.f.rs
│   │   │   ├── let_chains.f.rs
│   │   │   ├── let_else.f.rs
│   │   │   ├── negative_impls.f.rs
│   │   │   └── trait_alias.f.rs
│   │   ├── macro/
│   │   │   ├── attributes.f.rs
│   │   │   ├── macro.invocation.f.rs
│   │   │   ├── macro.item.f.rs
│   │   │   ├── macro.match.f.rs
│   │   │   ├── macro.tokens.f.rs
│   │   │   └── macro.transform.f.rs
│   │   ├── miscellaneous/
│   │   │   ├── ast-program-locs-attr-dangling.f.rs
│   │   │   ├── ast-program-locs-attr.f.rs
│   │   │   ├── ast-program-locs.f.rs
│   │   │   ├── empty-attr-dangling-x.f.rs
│   │   │   ├── empty-attr-dangling.f.rs
│   │   │   ├── empty-attr-x.f.rs
│   │   │   ├── empty-attr.f.rs
│   │   │   ├── empty-comment-block-x.f.rs
│   │   │   ├── empty-comment-block.f.rs
│   │   │   ├── empty-comment-x.f.rs
│   │   │   ├── empty-comment.f.rs
│   │   │   ├── empty-doc-block-x.f.rs
│   │   │   ├── empty-doc-block.f.rs
│   │   │   ├── empty-doc-x.f.rs
│   │   │   ├── empty-doc.f.rs
│   │   │   ├── empty.f.rs
│   │   │   ├── shebang-b.f.rs
│   │   │   └── shebang.f.rs
│   │   ├── patterns/
│   │   │   ├── pattern.f.rs
│   │   │   ├── rest.f.rs
│   │   │   └── union.f.rs
│   │   ├── specifiers/
│   │   │   ├── extern.f.rs
│   │   │   └── pub.f.rs
│   │   ├── statements/
│   │   │   ├── const.f.rs
│   │   │   ├── enum.f.rs
│   │   │   ├── impl.f.rs
│   │   │   ├── self.f.rs
│   │   │   ├── spread.f.rs
│   │   │   ├── statements.f.rs
│   │   │   ├── static.f.rs
│   │   │   ├── struct.f.rs
│   │   │   ├── trait.f.rs
│   │   │   ├── union.f.rs
│   │   │   └── use.f.rs
│   │   └── types/
│   │       ├── cast.f.rs
│   │       ├── never.f.rs
│   │       └── types.f.rs
│   ├── print.ts
│   ├── samples/
│   │   ├── comments/
│   │   │   ├── assignment.rs
│   │   │   ├── binaryish.rs
│   │   │   ├── blocks.rs
│   │   │   ├── chain.rs
│   │   │   ├── closure.rs
│   │   │   ├── dangling.rs
│   │   │   ├── file.rs
│   │   │   ├── flow.rs
│   │   │   ├── functions.rs
│   │   │   ├── ignore.attr.rs
│   │   │   ├── ignore.file.rs
│   │   │   ├── ignore.rs
│   │   │   ├── imports.rs
│   │   │   ├── macro.rs
│   │   │   ├── multiple.rs
│   │   │   ├── parens.rs
│   │   │   └── whitespace.rs
│   │   ├── common/
│   │   │   ├── arrays.rs
│   │   │   ├── assignments.rs
│   │   │   ├── binaryish.rs
│   │   │   ├── chains.first-argument-expansion.rs
│   │   │   ├── chains.last-argument-expansion.rs
│   │   │   ├── chains.rs
│   │   │   ├── closures.rs
│   │   │   ├── destructuring.rs
│   │   │   ├── members.rs
│   │   │   └── types.rs
│   │   ├── issues/
│   │   │   ├── 0.rs
│   │   │   ├── 14.rs
│   │   │   ├── 21/
│   │   │   │   ├── fn_comment.rs
│   │   │   │   ├── fn_fn.rs
│   │   │   │   ├── fn_ln.rs
│   │   │   │   ├── ln_fn_ln.rs
│   │   │   │   └── mod.rs
│   │   │   ├── 22.rs
│   │   │   ├── 25.rs
│   │   │   └── nth-pass.rs
│   │   ├── macros/
│   │   │   ├── cfg_if.rs
│   │   │   ├── if_chain.rs
│   │   │   └── matches.rs
│   │   └── styling/
│   │       ├── blockify.rs
│   │       ├── canInlineBlockBody.rs
│   │       ├── needsParens.rs
│   │       └── needsSemi.rs
│   └── test.build.ts
├── tsconfig.base.json
├── tsconfig.build.json
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/Bug_report.yml
================================================
name: ✨ Formatting
description: Issues for incorrect or unreadable code
body:
  - type: textarea
    id: code
    attributes:
      label: Input code
      placeholder: Paste the input code here.
      render: rust
    validations:
      required: true
  - type: textarea
    id: output
    attributes:
      label: Output code
      placeholder: Paste the formatted code here.
      render: rust
    validations:
      required: true
  - type: textarea
    id: extra
    attributes:
      label: Additional context
      placeholder: Add other relevant context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
contact_links:
  - name: Github discussion board
    url: https://github.com/jinxdash/prettier-plugin-rust/discussions
    about: Questions and answers
    
blank_issues_enabled: true


================================================
FILE: .gitignore
================================================
# npm
node_modules

# cache
/.rollup.cache/
*.tsbuildinfo

# debug
*.temp.*
*.temp/

# repl
/repl/

# build
/index.*
/extension/index.*
*.vsix
/crate/target

================================================
FILE: .gitmodules
================================================
[submodule "ext/jinx-rust"]
	path = ext/jinx-rust
	url = https://github.com/jinxdash/jinx-rust.git


================================================
FILE: .npmrc
================================================
link-workspace-packages = false

================================================
FILE: .vscode/launch.json
================================================
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
	"version": "0.2.0",
	"configurations": [
		{
			"args": ["--extensionDevelopmentPath=${workspaceFolder}/extension"],
			"name": "Launch Extension",
			"outFiles": ["${workspaceFolder}/extension/index.js"],
			"request": "launch",
			"type": "extensionHost"
		}
	]
}


================================================
FILE: .vscode/settings.json
================================================
{
  "npm.exclude": [
    "**/ext/jinx-rust"
  ],
  "files.exclude": {
    "ext": true
  }
}

================================================
FILE: CHANGELOG.md
================================================
# Prettier Rust Changelog

## Unreleased

- fix: clear some issues related to formatting `#[attr]` like full-on expressions ([#25](jinxdash/prettier-plugin-rust/issues/25))

## 0.1.9

- feat: format `cfg_if!` macros
- feat: format `@` character in `macro_rules`
- fix: format `<number>.` and `<number>e0` to float instead of int ([#14](https://github.com/jinxdash/prettier-plugin-rust/issues/14), [#16](https://github.com/jinxdash/prettier-plugin-rust/issues/16))
- fix: always end files with a newline ([#21](https://github.com/jinxdash/prettier-plugin-rust/issues/21))
- fix: avoid removing optional semi in rare [rust compiler bug](https://github.com/rust-lang/rust/issues/70844) ([#22](https://github.com/jinxdash/prettier-plugin-rust/issues/22))

## 0.1.8

- feat(extension): display message when formatting fails due to non-parser errors.
- feat: move `..spread`, `..` to the end of patterns and reassignments ([#7](https://github.com/jinxdash/prettier-plugin-rust/pull/7))
- fix: remove comma after `..` in patterns and reassignments ([#7](https://github.com/jinxdash/prettier-plugin-rust/pull/7))
- fix: support malformed `macro_rules!` ([jinx-rust@0.1.6](/jinxdash/jinx-rust/pull/2))
- fix: unprinted comment errors in failed macros ([#8](https://github.com/jinxdash/prettier-plugin-rust/pull/8))

## 0.1.7

- feat: move `..spread` to the end of struct literals ([#6](https://github.com/jinxdash/prettier-plugin-rust/pull/6))
- fix: remove comma after `..spread` in struct literals ([#6](https://github.com/jinxdash/prettier-plugin-rust/pull/6))

## 0.1.6

- fix: parenthesize >1 length `dyn`/`impl` types nested in unary types. ([#4](https://github.com/jinxdash/prettier-plugin-rust/pull/4))
- fix: remove comma after `match` cases with block-like macro expressions. ([#4](https://github.com/jinxdash/prettier-plugin-rust/pull/4))

## 0.1.5

- feat: wrap non-block closure expressions with a block when a `->` ReturnType is defined
- fix: add extension to filepath in ESM imports ([#2](https://github.com/jinxdash/prettier-plugin-rust/issues/2))
- fix: add missing whitespace in `let_else` feature
- fix(extension): disable config caching


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022-present jinxdash <jinxdash.github@gmail.com> (https://github.com/jinxdash)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================================================
FILE: README.md
================================================
<div align="center">
  <img alt="Prettier Rust" height="256px" src="https://user-images.githubusercontent.com/109366411/181039409-b66d6a4c-bbc7-4fbb-8a79-d7bb1af87a63.png">
</div>

<h1 align="center">Prettier Rust</h1>

<div align="center">

![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg) [![npm version](https://img.shields.io/npm/v/prettier-plugin-rust.svg?style=flat)](https://www.npmjs.com/package/prettier-plugin-rust) [![extension installs](https://img.shields.io/visual-studio-marketplace/i/jinxdash.prettier-rust?logo=visualstudiocode&style=social)](https://marketplace.visualstudio.com/items?itemName=jinxdash.prettier-rust) ![GitHub Repo stars](https://img.shields.io/github/stars/jinxdash/prettier-plugin-rust?style=social) [![Twitter Follow](https://img.shields.io/twitter/follow/jinxdash?style=social)](https://twitter.com/jinxdash)

_The massively popular [Prettier](https://prettier.io/) code formatter, now with [Rust](https://www.rust-lang.org/) support!_

**Get Started:** Install [VSCode Extension](https://marketplace.visualstudio.com/items?itemName=jinxdash.prettier-rust) `Prettier - Code formatter (Rust)`

</div>

## Why Prettier?

> What usually happens once people start using Prettier is that they realize how much time and mental energy they actually spend formatting their code. No matter how incomplete or broken the code you're working on is, with the Prettier Editor Extension you can always just press the `Format Document` key binding and \*poof\*, the code snaps right into place.

<br>

- **Beautiful, uniform and consistent** — Prettier is strongly opinionated, with no style options.
- **There when you need it the most** — Prettier can format code that won't compile _(e.g. missing annotations)_
- **Speed up the day-to-day** — Prettier auto-fixes common syntax errors _(e.g. missing semicolons, blocks, parentheses)_

<br>

<table align="center">
<tr> <th>> input</th> <th>> formatted</th> </tr>
<tr>
  <td>

<!-- prettier-ignore -->
```rs
const LEET = 1337
/// My WIP code draft
#![feature(crate_visibility_modifier)]
async crate fn foo(arg) {
  arg.0 *= 3.14 + LEET & 1337
  arg.1(|b, c| -> T &c).await
}
```

  </td>
  <td>

<!-- prettier-ignore -->
```rs
const LEET = 1337;
#![feature(crate_visibility_modifier)]
/// My WIP code draft
crate async fn foo(arg) {
    arg.0 *= (3.14 + LEET) & 1337;
    (arg.1)(|b, c| -> T { &c }).await
}
```

  </td>
</tr>
</table>
<div align="center">

_Formatting succeeds and fixes 7 syntax errors._

</div>

<br>

## Configuration

https://prettier.io/docs/en/configuration

<!-- prettier-ignore -->
```json5
// .prettierrc.json
{
  "useTabs": false,
  "tabWidth": 4,
  "printWidth": 100,
  "endOfLine": "lf",

  // -- Not supported yet --
  // "trailingComma": "es5",
  // "embeddedLanguageFormatting": "auto",

  // Example override
  "overrides": { "files": ["tests/*.rs"], "options": { "printWidth": 80 } }
}
```

<details>
    <summary>See alternative configuration using a TOML file</summary>

```toml
# .prettierrc.toml

useTabs = false
tabWidth = 4
printWidth = 100
endOfLine = "lf"

# -- Not supported yet --
# trailingComma = "es5"
# embeddedLanguageFormatting = "auto"

# Example override
overrides = [
  { files = ["tests/*.rs"], options = { printWidth = 80 } }
]
```

</details>

### How to ignore things

- Add `// prettier-ignore` or `#[rustfmt::skip]` above it
- Add `#![rustfmt::skip]` inside blocks or files
- Create a `.prettierignore` file to glob-match files, like `.gitignore`

### How are macros formatted?

- Curlies `!{}` format like blocks, `![]` and `!()` like comma-separated expressions
- Formatting inside macro invocations is more conservative, since macros can be token-sensitive
- Popular/built-in macros with original syntax rules get custom formatting (e.g. `matches!`, `if_chains!`...) _[Not implemented yet]_
- Macro Declarations are only partially formatted (the transformed part isn't yet, but could be in the future)
- Macros that can't be formatted are silently ignored

### Are nightly features supported?

Yes! Prettier Rust formats most nightly features. Support depends on [`jinx-rust`](https://github.com/jinxdash/jinx-rust).

<br>

## Editor integration

- ### `Recommended` Extension Standalone

  _Easy install + auto-updates_

  - VSCode | Search and install `Prettier - Code formatter (Rust)` [[direct link]](https://marketplace.visualstudio.com/items?itemName=jinxdash.prettier-rust)

  - _Request your favorite editor:_ [[file an issue]](https://github.com/jinxdash/prettier-plugin-rust/issues/new)

- ### `Alternative` Core Extension Plugin

  _Requires [NodeJS](https://nodejs.dev/download/) + [Prettier Extension](https://prettier.io/docs/en/editors.html)_ (built-in Jetbrains IDEs)

  ```sh
  npm install --global prettier-plugin-rust prettier
  ```

  _Restart IDE after installing._  
  _To update (manual only!!):_ `npm upgrade --global prettier-plugin-rust prettier`  
  _To check installed version:_ `npm ls -g --depth=0 prettier-plugin-rust prettier`  
  _To check latest version:_ `npm info prettier-plugin-rust version`

<br>

## Project integration

- ### Command line

  _Requires [NodeJS](https://nodejs.dev/download/)_

  - Install `prettier` and `prettier-plugin-rust` globally

    ```sh
    npm install --global prettier-plugin-rust prettier
    ```

  - Use the [prettier CLI](https://prettier.io/docs/en/cli.html) to format rust files. E.g. run:

    ```sh
    prettier --write **/*.rs
    ```

- ### NodeJS package

  _Requires [NodeJS](https://nodejs.dev/download/)_

  - Install `prettier` and `prettier-plugin-rust` in the project

    ```sh
    npm install --save-dev prettier-plugin-rust prettier
    ```

  - Link to the plugin's location in your prettier config:

    ```json
    "plugins": ["./node_modules/prettier-plugin-rust"]
    ```

  - Use the [prettier CLI](https://prettier.io/docs/en/cli.html) to format rust files. E.g. run:

    ```sh
    npx prettier --write **/*.rs
    ```

  - You can also use the plugin programmatically:

    ```ts
    import prettier from "prettier";
    import * as rustPlugin from "prettier-plugin-rust";

    prettier.format(code, { plugins: [rustPlugin] });
    ```

- ### Rust crate

  _No crate yet. Above options are available in the meantime._

<br>

## Q&A

- ### _Why would I use this and not the established `cargo fmt` ?_

  _It's all about the Editor Integration_ — Having the ability to format your code while you work on it really makes for a great developer experience, and autocompletion for Rust's strict syntax is such a massive time save. Once you've tried the extension there really is no coming back.

  All-in-all the difference in code style is minimal, so adopting Prettier Rust won't drastically change your codebase. The real downside is the harsher integration with the Rust ecosystem, but it'll get better eventually.

  Point by point:

  - the extension streamlines your work in the editor
    - it can format code that won't compile _(e.g. code with missing type annotations)_
    - it autocorrects syntax errors _(e.g. missing semicolons, blocks, parentheses...)_
  - it is strongly opinionated with no style options, so code is uniform across projects.
  - it produces more readable code in some cases (e.g. condition chains, compound expressions, patterns)
  - it supports everything out-of-the-box (e.g. nightly features, macros)
  - it consistently prints code in the same way, whereas Rustfmt preserves arbitrary style at places
  - it can be used for other languages (e.g. markdown, html, typescript, java, python, ruby)
  - it formats language embeds. So rust code blocks in non-rust files (e.g. markdown), and supported languages in rust doc comments. _[NOTE: the latter is not yet implemented]_

- ### _Why not just add those features to rustfmt instead?_

  Unfortunately Rustfmt cannot implement those features by design.

  Rustfmt parses code with rustc. Rustc is strict and unforgiving as it always assumes code is at its "final version", thus every slight deviation from the accepted syntax crashes the parser. There's also that rustc has many lint-like checks within the parser. The intention is to save work for the compiler down the line, unfortunately it also means that rustc sometimes fails to parse syntactically correct code.

  Prettier Rust however is based on [jinx-rust](https://github.com/jinxdash/jinx-rust). Jinx-rust is built specifically for Rust tooling. Hence it's designed to tolerate a wide range of syntax errors, supports missing nodes and sometimes even infers user intent (e.g. Javascript's `!==`)

  Jinx-rust has a little _plaidoyer_ in its readme arguing for Rust Tooling _not_ to use the official rustc parser [here](https://github.com/jinxdash/jinx-rust#why-jinx-rust-and-why-in-typescript).

- ### _When exactly does Prettier Rust change code syntax?_

  The Prettier Rust syntax autocorrection feature is intended to be an adaptation of how Prettier Typescript autocorrects javascript code with missing semicolons.

  You can effectively think of Prettier Rust syntax autocorrection as auto-applying the Rust compiler's suggested syntax fixes automatically (e.g. "semicolon missing here", "parenthesize this" or "add a block around that")

  Otherwise if your codebase compiles, then Prettier Rust is just a formatter like any other. It won't change the syntax of valid rust code. Moreover, it doesn't reorganize imports, split comments or combine attributes.

- ### _This is an "opinionated formatter". But it's brand new! How reliable are those opinions?_

  Rest assured, Prettier Rust actually does not take style decisions on its own. Prettier Rust is essentially a 1:1 adaptation of Prettier Typescript, hence the opinions it implements have been battle tested and agreed-upon by [millions and millions of users](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) already.


================================================
FILE: crate/Cargo.toml
================================================
[package]
name = "prettier"
version = "0.1.5"
edition = "2021"
readme = "README.md"
license = "MIT"
description = "Opinionated code formatter - `cargo fmt` alternative (Community) (WIP)"
repository = "https://github.com/jinxdash/prettier-plugin-rust"
keywords = ["format", "rustfmt", "tool", "development", "editor"]
categories = ["development-tools", "text-editors", "web-programming"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]


================================================
FILE: crate/README.md
================================================
# Prettier crate

_Reserved_ (August 2022)

https://github.com/jinxdash/prettier-plugin-rust

- _(todo) Crate provides Prettier CLI_
- _(todo) Rust binding for `prettier.format`_
- _(todo) Configuration and version-control of prettier and its plugins from `Cargo.toml`_

================================================
FILE: crate/src/main.rs
================================================
fn main() {
    println!("Hello, world!");
}


================================================
FILE: extension/LICENSE
================================================
MIT License

Copyright (c) 2022-present jinxdash <jinxdash.github@gmail.com> (https://github.com/jinxdash)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================================================
FILE: extension/README.md
================================================
<div align="center">
  <img alt="Prettier Rust" height="256px" src="https://user-images.githubusercontent.com/109366411/181039409-b66d6a4c-bbc7-4fbb-8a79-d7bb1af87a63.png">
</div>

<h1 align="center">Prettier Rust</h1>

<div align="center">

![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg) [![npm version](https://img.shields.io/npm/v/prettier-plugin-rust.svg?style=flat)](https://www.npmjs.com/package/prettier-plugin-rust) ![GitHub Repo stars](https://img.shields.io/github/stars/jinxdash/prettier-plugin-rust?style=social) [![Twitter Follow](https://img.shields.io/twitter/follow/jinxdash?style=social)](https://twitter.com/jinxdash)

_The massively popular [Prettier](https://prettier.io/) code formatter, now with [Rust](https://www.rust-lang.org/) support!_

_This extension is a standalone bundle of Prettier + Prettier Plugin Rust._

</div>

## Why Prettier?

> What usually happens once people start using Prettier is that they realize how much time and mental energy they actually spend formatting their code. No matter how incomplete or broken the code you're working on is, with the Prettier Editor Extension you can always just press the `Format Document` key binding and \*poof\*, the code snaps right into place.

<br>

- **Beautiful, uniform and consistent** — Prettier is strongly opinionated, with no style options.
- **There when you need it the most** — Prettier can format code that won't compile _(e.g. missing annotations)_
- **Speed up the day-to-day** — Prettier auto-fixes common syntax errors _(e.g. missing semicolons, blocks, parentheses)_

<br>

<table align="center">
<tr> <th>> input</th> <th>> formatted</th> </tr>
<tr>
  <td>

<!-- prettier-ignore -->
```rs
const LEET = 1337
/// My WIP code draft
#![feature(crate_visibility_modifier)]
async crate fn foo(arg) {
  arg.0 *= 3.14 + LEET & 1337
  arg.1(|b, c| -> T &c).await
}
```

  </td>
  <td>

<!-- prettier-ignore -->
```rs
const LEET = 1337;
#![feature(crate_visibility_modifier)]
/// My WIP code draft
crate async fn foo(arg) {
    arg.0 *= (3.14 + LEET) & 1337;
    (arg.1)(|b, c| -> T { &c }).await
}
```

  </td>
</tr>
</table>
<div align="center">

_Formatting succeeds and fixes 7 syntax errors._

</div>

<br>

## Configuration

https://prettier.io/docs/en/configuration

<!-- prettier-ignore -->
```json5
// .prettierrc.json
{
  "useTabs": false,
  "tabWidth": 4,
  "printWidth": 100,
  "endOfLine": "lf",

  // -- Not supported yet --
  // "trailingComma": "es5",
  // "embeddedLanguageFormatting": "auto",

  // Example override
  "overrides": { "files": ["tests/*.rs"], "options": { "printWidth": 80 } }
}
```

<details>
    <summary>Alternative config using TOML</summary>

```toml
# .prettierrc.toml

useTabs = false
tabWidth = 4
printWidth = 100
endOfLine = "lf"

# -- Not supported yet --
# trailingComma = "es5"
# embeddedLanguageFormatting = "auto"

# Example override
overrides = [
  { files = ["tests/*.rs"], options = { printWidth = 80 } }
]
```

</details>

### How to ignore things

- Add `// prettier-ignore` or `#[rustfmt::skip]` above it
- Add `#![rustfmt::skip]` inside blocks or files
- Create a `.prettierignore` file to glob-match files, like `.gitignore`

### How are macros formatted?

- Curlies `!{}` format like blocks, `![]` and `!()` like comma-separated expressions
- Formatting inside macro invocations is more conservative, since macros can be token-sensitive
- Popular/built-in macros with original syntax rules get custom formatting (e.g. `matches!`, `if_chains!`...) _[Not implemented yet]_
- Macro Declarations are only partially formatted (the transformed part isn't yet, but could be in the future)
- Macros that can't be formatted are silently ignored

### Are nightly features supported?

Yes! Prettier Rust formats most nightly features. Support depends on [`jinx-rust`](https://github.com/jinxdash/jinx-rust).

<br>

## Project integration

- ### Command line

  _Requires [NodeJS](https://nodejs.dev/download/)_

  - Install `prettier` and `prettier-plugin-rust` globally

    ```sh
    npm install --global prettier-plugin-rust prettier
    ```

  - Use the [prettier CLI](https://prettier.io/docs/en/cli.html) to format rust files. E.g. run:

    ```sh
    prettier --write **/*.rs
    ```

- ### NodeJS package

  _Requires [NodeJS](https://nodejs.dev/download/)_

  - Install `prettier` and `prettier-plugin-rust` in the project

    ```sh
    npm install --save-dev prettier-plugin-rust prettier
    ```

  - Link to the plugin's location in your prettier config:

    ```json
    "plugins": ["./node_modules/prettier-plugin-rust"]
    ```

  - Use the [prettier CLI](https://prettier.io/docs/en/cli.html) to format rust files. E.g. run:

    ```sh
    npx prettier --write **/*.rs
    ```

- ### Rust crate

  _No crate yet. Above options are available in the meantime._

<br>

## Q&A

- ### _Why would I use this and not the established `cargo fmt` ?_

  _It's all about the Editor Integration_ — Having the ability to format your code while you work on it really makes for a great developer experience, and autocompletion for Rust's strict syntax is such a massive time save. Once you've tried the extension there really is no coming back.

  All-in-all the difference in code style is minimal, so adopting Prettier Rust won't drastically change your codebase. The real downside is the harsher integration with the Rust ecosystem, but it'll get better eventually.

  Point by point:

  - the extension streamlines your work in the editor
    - it can format code that won't compile _(e.g. code with missing type annotations)_
    - it autocorrects syntax errors _(e.g. missing semicolons, blocks, parentheses...)_
  - it is strongly opinionated with no style options, so code is uniform across projects.
  - it produces more readable code in some cases (e.g. condition chains, compound expressions, patterns)
  - it supports everything out-of-the-box (e.g. nightly features, macros)
  - it consistently prints code in the same way, whereas Rustfmt preserves arbitrary style at places
  - it can be used for other languages (e.g. markdown, html, typescript, java, python, ruby)
  - it formats language embeds. So rust code blocks in non-rust files (e.g. markdown), and supported languages in rust doc comments. _[NOTE: the latter is not yet implemented]_

- ### _Why not just add those features to rustfmt instead?_

  Unfortunately Rustfmt cannot implement those features by design.

  Rustfmt parses code with rustc. Rustc is strict and unforgiving as it always assumes code is at its "final version", thus every slight deviation from the accepted syntax crashes the parser. There's also that rustc has many lint-like checks within the parser. The intention is to save work for the compiler down the line, unfortunately it also means that rustc sometimes fails to parse syntactically correct code.

  Prettier Rust however is based on [jinx-rust](https://github.com/jinxdash/jinx-rust). Jinx-rust is built specifically for Rust tooling. Hence it's designed to tolerate a wide range of syntax errors, supports missing nodes and sometimes even infers user intent (e.g. Javascript's `!==`)

  Jinx-rust has a little *plaidoyer* in its readme arguing for Rust Tooling *not* to use the official rustc parser [here](https://github.com/jinxdash/jinx-rust#why-jinx-rust-and-why-in-typescript).

- ### _When exactly does Prettier Rust change code syntax?_

  The Prettier Rust syntax autocorrection feature is intended to be an adaptation of how Prettier Typescript autocorrects javascript code with missing semicolons.

  You can effectively think of Prettier Rust syntax autocorrection as auto-applying the Rust compiler's suggested syntax fixes automatically (e.g. "semicolon missing here", "parenthesize this" or "add a block around that")

  Otherwise if your codebase compiles, then Prettier Rust is just a formatter like any other. It won't change the syntax of valid rust code. Moreover, it doesn't reorganize imports, split comments or combine attributes.

- ### _This is an "opinionated formatter". But it's brand new! How reliable are those opinions?_

  Rest assured, Prettier Rust actually does not take style decisions on its own. Prettier Rust is essentially a 1:1 adaptation of Prettier Typescript, hence the opinions it implements have been battle tested and agreed-upon by [millions and millions of users](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) already.


================================================
FILE: extension/package.json
================================================
{
	"icon": "icon.png",
	"name": "prettier-rust",
	"publisher": "jinxdash",
	"displayName": "Prettier - Code formatter (Rust)",
	"description": "Prettier Rust is a code formatter that autocorrects bad syntax",
	"repository": {
		"type": "git",
		"url": "https://github.com/jinxdash/prettier-plugin-rust.git",
		"directory": "extension"
	},
	"author": "jinxdash <jinxdash.github@gmail.com> (https://github.com/jinxdash)",
	"version": "0.1.9",
	"engines": {
		"vscode": "^1.69.0"
	},
	"categories": [
		"Formatters"
	],
	"keywords": [
		"rust",
		"formatter",
		"prettier"
	],
	"scripts": {
		"bundle": "esbuild src/index.ts --bundle --outfile=index.js --format=cjs --platform=node --external:vscode",
		"watch": "pnpm run bundle --watch",
		"package": "pnpm run bundle && pnpm vsce package --no-dependencies",
		"publish": "pnpm vsce publish --no-dependencies"
	},
	"contributes": {
		"languages": [
			{
				"id": "rust",
				"aliases": [
					"rs"
				],
				"extensions": [
					"rs"
				]
			}
		]
	},
	"devDependencies": {
		"@types/node": "^18.0.6",
		"@types/vscode": "^1.69.0",
		"esbuild": "^0.14.49",
		"typescript": "^4.7.4"
	},
	"dependencies": {
		"prettier": "^2.7.1",
		"jinx-rust": "0.1.6",
		"prettier-plugin-rust": "workspace:prettier-plugin-rust"
	},
	"main": "index.js",
	"activationEvents": [
		"onStartupFinished"
	]
}


================================================
FILE: extension/src/index.ts
================================================
import { rs } from "jinx-rust";
import path from "node:path";
import prettier, { Config, resolveConfig } from "prettier";
import plugin from "prettier-plugin-rust";
import { ExtensionContext, languages, Position, Range, TextDocument, TextEdit, window } from "vscode";

const console = createOutputChannel("Prettier (Rust)");

console.log(`VSCode Extension: ${process.env.EXTENSION_NAME || "jinxdash.prettier-rust"}@${process.env.EXTENSION_VERSION || "dev"}`);
export async function activate(context: ExtensionContext) {
	context.subscriptions.push(
		languages.registerDocumentFormattingEditProvider("rust", {
			async provideDocumentFormattingEdits(document) {
				const config: Config = {
					filepath: cmd(document.fileName),
					...((await resolveConfig(document.fileName, { editorconfig: true, useCache: false })) ?? {}),
				};
				console.log("", `# Formatting using prettier@${prettier.version}`, {
					...config,
					plugins: config.plugins?.flatMap((p) => (typeof p === "string" ? p : p.printers && Object.keys(p.printers))),
				});
				if (
					!config.plugins?.some((plugin) => typeof plugin === "object" && plugin.languages?.some((lang) => lang.name === "Rust"))
				) {
					config.parser = "jinx-rust";
					config.plugins ??= [plugin];
				}
				return format(document, config);
			},
		})
	);
}

function format(document: TextDocument, config: Config) {
	// length of common prefix
	const next = tryFormat(document, config);
	const prev = document.getText();
	if (prev === next) {
		// console.log("No changes");
		return [];
	}
	const end = Math.min(prev.length, next.length);
	var i = 0;
	var j = 0;
	for (var i = 0; i < end && compare(i, i); ++i);
	for (var j = 0; i + j < end && compare(prev.length - j - 1, next.length - j - 1); ++j);
	// console.log([i, j]);
	return [TextEdit.replace(new Range(document.positionAt(i), document.positionAt(prev.length - j)), next.substring(i, next.length - j))];
	function compare(i: number, j: number) {
		return prev.charCodeAt(i) === next.charCodeAt(j);
	}
}

function tryFormat(doc: TextDocument, config: prettier.Config) {
	try {
		return prettier.format(doc.getText(), config);
	} catch (e) {
		if ((e as any).loc) {
			try {
				rs.parseFile(doc.getText(), { filepath: config.filepath });
			} catch (_e) {
				const e2 = _e as rs.ParserError;
				const pos = new Position(e2.loc.start.line - 1, e2.loc.start.column - 1);
				window.showTextDocument(doc, { selection: new Range(pos, pos) });
				window.showErrorMessage(e2.message);
			}
		} else {
			console.log(e);
			window.showErrorMessage((e as any).message);
		}
		return doc.getText();
	}
}

function createOutputChannel(name: string) {
	const out = window.createOutputChannel(name);
	return {
		log(...arr: any[]) {
			for (var item of arr) {
				if (typeof item === "string") {
					out.appendLine(unstyle(item));
				} else if (item instanceof Error) {
					if (item?.message) out.appendLine(unstyle(item.message));
					if (item?.stack) out.appendLine(unstyle(item.stack));
				} else {
					out.appendLine(JSON.stringify(item, null, 2));
				}
			}
		},
	};
}
function unstyle(str: string) {
	return str.replace(/\x1B\[[0-9][0-9]?m/g, "");
}

function cmd(filepath: string | undefined, frompath = "") {
	return normPath(path.relative(frompath, normPath(filepath ?? ""))) || ".";
}
function normPath(filepath: string) {
	return filepath.replace(/^file:\/\/\//, "").replace(/\\\\?/g, "/");
}


================================================
FILE: extension/tsconfig.json
================================================
{
	"extends": "../tsconfig.base.json",
	"compilerOptions": { "rootDir": "src" },
	"include": ["src"]
}


================================================
FILE: package.json
================================================
{
	"name": "prettier-plugin-rust",
	"version": "0.1.9",
	"description": "Prettier plugin for Rust",
	"repository": {
		"type": "git",
		"url": "https://github.com/jinxdash/prettier-plugin-rust.git"
	},
	"author": "jinxdash <jinxdash.github@gmail.com> (https://github.com/jinxdash)",
	"keywords": [
		"prettier",
		"formatter",
		"rust"
	],
	"license": "MIT",
	"type": "module",
	"main": "index.cjs",
	"module": "index.js",
	"types": "index.d.ts",
	"exports": {
		"./package.json": "./package.json",
		".": {
			"require": "./index.cjs",
			"import": "./index.js"
		}
	},
	"files": [
		"index.js",
		"index.cjs",
		"index.d.ts",
		"package.json",
		"LICENSE"
	],
	"scripts": {
		"build": "ts-node ./scripts/build.ts && ts-node ./tests/test.build.ts",
		"test-build": "ts-node ./tests/test.build.ts",
		"test-print-samples": "ts-node ./tests/print.ts",
		"dev-repl": "ts-node ./scripts/dev.repl.ts",
		"dev-format-local": "ts-node ./scripts/dev.format.ts"
	},
	"devDependencies": {
		"@swc/core": "^1.2.248",
		"@types/node": "^18.0.6",
		"@types/prettier": "^2.7.0",
		"ts-node": "^10.9.1",
		"tsup": "^6.2.3",
		"typescript": "^4.8.2"
	},
	"dependencies": {
		"jinx-rust": "0.1.6",
		"prettier": "^2.7.1"
	},
	"prettier": {
		"printWidth": 140,
		"semi": true,
		"tabWidth": 4,
		"useTabs": true,
		"endOfLine": "lf",
		"overrides": [
			{
				"files": [
					"**/*.md"
				],
				"options": {
					"printWidth": 80,
					"useTabs": false,
					"tabWidth": 2
				}
			}
		]
	}
}


================================================
FILE: pnpm-workspace.yaml
================================================
packages:
  - 'ext/*'
  - 'extension'

================================================
FILE: scripts/build.ts
================================================
import { build } from "tsup";
import { createStripPlugin } from "../ext/jinx-rust/scripts/utils/build";

await build({
	dts: true,
	tsconfig: "tsconfig.build.json",
	entry: ["src/index.ts"],
	external: ["jinx-rust"],
	outDir: ".",
	format: ["cjs", "esm"],
	plugins: [createStripPlugin({ labels: ["__DEV__"], functionCalls: ["devonly"] })],
	treeshake: {
		preset: "smallest",
		moduleSideEffects: false,
		propertyReadSideEffects: false,
		tryCatchDeoptimization: false,
		unknownGlobalSideEffects: false,
	},
});


================================================
FILE: scripts/dev.format.ts
================================================
import prettier from "prettier";
import { for_each_rs_file } from "../ext/jinx-rust/scripts/utils/common";
import { update_file } from "../ext/jinx-rust/scripts/utils/fs";
import { plugin } from "../src/format/plugin";

function format(code: string, filepath: string) {
	const next = prettier.format(code, {
		parser: "jinx-rust",
		plugins: [plugin],
		printWidth: 100,
		tabWidth: 4,
		filepath,
	});
	if (code !== next && code.trim() !== next.trim()) {
		update_file(filepath, next, { external: true });
	}
}

globalThis.TESTS_FORMAT_DEV = true;

for_each_rs_file(
	[`E:/dev/github/rust/rust-lang/`],
	(file) => {
		format(file.content, file.cmd);
	},
	["test", "tests"]
).then(() => {
	globalThis.TESTS_FORMAT_DEV = false;
});


================================================
FILE: scripts/dev.repl.ts
================================================
import { createASTtoJSONPrinter, createPrettierPrinter, rs_createREPL } from "../ext/jinx-rust/scripts/utils";
import { plugin } from "../src/format/plugin";

rs_createREPL([
	createPrettierPrinter(
		{
			parser: "jinx-rust",
			plugins: [plugin],
		},
		true
	),
	createASTtoJSONPrinter(),
]);


================================================
FILE: scripts/tsconfig.json
================================================
{
	"extends": "../tsconfig.base.json",
	"include": ["../tests", "../scripts"],
	"references": [{ "path": "../tsconfig.json" }, { "path": "../ext/jinx-rust/scripts/tsconfig.json" }],
	"compilerOptions": {
		"rootDir": ".."
	}
}


================================================
FILE: src/format/comments.ts
================================================
import { CommentOrDocComment, LocArray, Node, NodeType, NodeWithBodyOrCases } from "jinx-rust";
import {
	end,
	getBodyOrCases,
	getLastParameter,
	hasOuterAttributes,
	isInner,
	is_Attribute,
	is_AttributeOrDocComment,
	is_BlockCommentKind,
	is_BlockCommentNode,
	is_Comment,
	is_CommentOrDocComment,
	is_ExpressionWithBodyOrCases,
	is_ExternSpecifier,
	is_FlowControlExpression,
	is_FunctionDeclaration,
	is_FunctionNode,
	is_IfBlockExpression,
	is_LineCommentKind,
	is_LineCommentNode,
	is_LocArray,
	is_MacroRule,
	is_NodeWithBodyOrCases,
	is_ReassignmentNode,
	is_StatementNode,
	is_StructLiteralProperty,
	is_StructLiteralPropertySpread,
	nisAnyOf,
	ownStart,
	start,
} from "jinx-rust/utils";
import { is_CallExpression_or_CallLikeMacroInvocation } from "../transform";
import { Narrow, assert, exit, iLast, last_of, maybe_last_of } from "../utils/common";
import { is_MemberAccessLike, is_xVariableEqualishLike } from "./core";
import {
	AnyComment,
	CustomOptions,
	DCM,
	Doc,
	MutatedAttribute,
	NodeWithComments,
	PrettierCommentInfo,
	breakParent,
	cursor,
	hardline,
	indent,
	join,
	line,
	lineSuffix,
	literalline,
} from "./external";
import { assertPathAtNode, canAttachComment, getAllComments, getContext, getNode, getOptions, pathCallEach } from "./plugin";
import { shouldPrintOuterAttributesAbove } from "./styling";

function addCommentHelper(node: Node, comment: AnyComment, leading = false, trailing = false) {
	__DEV__: assert(!handled(comment));
	((node as NodeWithComments<Node>).comments ??= []).push(comment);
	(comment.leading = leading), (comment.trailing = trailing), (comment.printed = false);
}

function addLeadingComment(node: Node, comment: AnyComment) {
	addCommentHelper(node, comment, true);
}
function addDanglingComment(node: Node, comment: AnyComment, marker: DCM) {
	addCommentHelper(node, comment);
	comment.marker = marker;
}
function addTrailingComment(node: Node, comment: AnyComment) {
	addCommentHelper(node, comment, false, true);
}
export function setPrettierIgnoreTarget(node: Node, comment: AnyComment) {
	__DEV__: Narrow<Node & { prettierIgnore?: true }>(node), assert(isPrettierIgnoreComment(comment) || isPrettierIgnoreAttribute(comment));
	comment.unignore = true;
	node.prettierIgnore = true;
}

function hasComments<T extends Node>(node: T): node is NodeWithComments<T> {
	return "comments" in node && node.comments.length > 0;
}

export function printDanglingComments(enclosingNode: Node, sameIndent: boolean, marker?: DCM) {
	if (hasComments(enclosingNode)) {
		const printed: Doc[] = [];
		pathCallEach(enclosingNode, "comments", (comment) => {
			if (isDangling(comment) && (!marker || comment.marker === marker)) {
				printed.push(printComment(comment));
			}
		});
		if (printed.length > 0) {
			return sameIndent //
				? join(hardline, printed)
				: indent([hardline, join(hardline, printed)]);
		}
	}
	return "";
}

export function needsHardlineAfterDanglingComment(node: Node) {
	if (!hasComment(node)) return false;
	const lastDanglingComment = maybe_last_of(getComments(node, CF.Dangling));
	return lastDanglingComment && is_LineCommentNode(lastDanglingComment);
}
export function setDidPrintComment(comment: AnyComment) {
	comment.printed = true;
}

function printComment(comment: AnyComment) {
	__DEV__: assertPathAtNode("printComment", comment);
	__DEV__: assert(handled(comment), `Assertion failed: Comment was not printed at ${comment.loc.url()}`, comment);
	setDidPrintComment(comment);
	return getContext().options.printer.printComment!(getContext().path as any, getOptions());
}

export function isPreviousLineEmpty(node: Node) {
	let index = start(node) - 1;
	index = skipSpaces(index, true) as number;
	index = skipNewline(index, true) as number;
	index = skipSpaces(index, true) as number;
	return index !== skipNewline(index, true);
}
export function hasBreaklineBefore(node: Node) {
	return hasNewline(start(node) - 1, true);
}

export function hasBreaklineAfter(node: Node) {
	return hasNewline(end(node));
}

export function printCommentsSeparately(ignored?: Set<AnyComment>) {
	const node = getNode();
	__DEV__: Narrow<Node & { comments?: AnyComment[] }>(node);

	const leading: Doc[] = [];
	const trailing: Doc[] = [];
	let hasTrailingLineComment = false;
	let hadLeadingBlockComment = false;

	if ("comments" in node) {
		pathCallEach(node, "comments", (comment) => {
			if (ignored?.has(comment)) {
				return;
			} else if (isLeading(comment)) {
				leading.push(printLeadingComment(comment));
			} else if (isTrailing(comment)) {
				trailing.push(printTrailingComment(comment));
			}
		});
	}

	if (node === getOptions().cursorNode) {
		leading.unshift(cursor);
		trailing.push(cursor);
	}

	return (leading.length | trailing.length) > 0 ? { leading, trailing } : ({ leading: "", trailing: "" } as const);

	function printLeadingComment(comment: AnyComment) {
		if (is_Attribute(comment) && !comment.inner) {
			const printed = printComment(comment);
			return [printed, " "];
		}
		hadLeadingBlockComment ||= is_BlockCommentKind(comment) && hasBreaklineBefore(comment);
		return [
			printComment(comment),
			is_BlockCommentKind(comment)
				? hasBreaklineAfter(comment) //
					? hadLeadingBlockComment
						? hardline
						: line
					: " "
				: hardline,
			hasNewline(skipNewline(skipSpaces(end(comment)))) ? hardline : "",
		];
	}

	function printTrailingComment(comment: AnyComment) {
		const printed = printComment(comment);
		return hasBreaklineBefore(comment)
			? lineSuffix([hardline, isPreviousLineEmpty(comment) ? hardline : "", printed])
			: is_BlockCommentNode(comment)
			? [" ", printed]
			: lineSuffix([" ", printed, hasTrailingLineComment === (hasTrailingLineComment = true) ? hardline : breakParent]);
	}
}

export function getPostLeadingComment(comment: AnyComment) {
	// console.log(comment.loc.url());
	// is_BlockCommentKind(comment)
	// 	? hasBreaklineAfter(comment) //
	// 		? hasBreaklineBefore(comment)
	// 			? hardline
	// 			: line
	// 		: " "
	// 	: hardline,
	return hasNewline(skipNewline(skipSpaces(end(comment)))) ? hardline : "";
}

export function withComments<D extends Doc>(node: Node, printed: D, ignored?: Set<AnyComment>): D | Doc[] {
	__DEV__: assertPathAtNode("withComments", node);
	const { leading, trailing } = printCommentsSeparately(ignored);
	return leading || trailing ? [...leading!, printed, ...trailing!] : printed;
	// return needsOuterParens(node) ? group(["(", indent([softline, parts]), softline, ")"]) : parts;
	// return parts;
}
export function getComments(node: Node, ...args: Parameters<typeof getCommentTestFunction>): AnyComment[] {
	__DEV__: Narrow<Node & { comments?: AnyComment[] }>(node);
	// if (!node || !node.comments) return [];
	// if (args.length === 0) return node.comments;
	// return args.length > 0 ? node.comments.filter(getCommentTestFunction(...args)) : node.comments;
	return node && node.comments //
		? args.length > 0
			? node.comments.filter(getCommentTestFunction(...args))
			: node.comments
		: [];
}

export function getFirstComment(node: Node, flags: CF, fn?: (comment: AnyComment) => boolean): AnyComment | undefined {
	const r = getComments(node, flags | CF.First, fn);
	return r.length === 0 ? undefined : r[0];
}

export function escapeComments(flags: number, fn?: (comment: AnyComment) => boolean) {
	const comments = getAllComments().filter(getCommentTestFunction(flags, fn)) as AnyComment[];
	comments.forEach(setDidPrintComment);
	return new Set(comments);
}

export const enum CF {
	Leading = 1 << 1,
	Trailing = 1 << 2,
	Dangling = 1 << 3,
	Block = 1 << 4,
	Line = 1 << 5,
	PrettierIgnore = 1 << 6,
	First = 1 << 7,
	Last = 1 << 8,
}
export function isPrettierIgnoreComment(comment: AnyComment) {
	return is_Comment(comment) && /^\s*prettier-ignore\s*/.test(comment.value) && !comment.unignore;
}
export function isPrettierIgnoreAttribute(node: Node): node is MutatedAttribute {
	return is_Attribute(node) && /^\s*rustfmt::skip\s*$/.test(node.value);
}
function getCommentTestFunction(flags: CF, fn?: (comment: AnyComment) => boolean) {
	return function (comment: AnyComment, index: number, comments: AnyComment[]) {
		__DEV__: Narrow<number>(flags), assert(handled(comment));
		return !(
			(flags & CF.Leading && !isLeading(comment)) ||
			(flags & CF.Trailing && !isTrailing(comment)) ||
			(flags & CF.Dangling && !isDangling(comment)) ||
			(flags & CF.Block && !is_BlockCommentKind(comment)) ||
			(flags & CF.Line && !is_LineCommentKind(comment)) ||
			(flags & CF.First && index !== 0) ||
			(flags & CF.Last && !iLast(index, comments)) ||
			(flags & CF.PrettierIgnore && !(isPrettierIgnoreComment(comment) || isPrettierIgnoreAttribute(comment))) ||
			(fn && !fn(comment))
		);
	};
}

export function hasComment(node: Node, flags: number = 0, fn?: (comment: AnyComment) => boolean) {
	if ("comments" in node && node.comments!.length > 0) {
		return flags || fn ? (node.comments as AnyComment[]).some(getCommentTestFunction(flags, fn)) : true;
	}
	return false;
}
export function hasNewlineInRange(leftIndex: number, rightIndex: number) {
	__DEV__: assert(leftIndex <= rightIndex);
	const text = getContext().options.originalText;
	for (var i = leftIndex; i < rightIndex; ++i) if (text.charCodeAt(i) === 10) return true;
	return false;
}
export function isNextLineEmpty(node: Node) {
	return isNextLineEmptyAfterIndex(end(node));
}
export function isNextLineEmptyAfterIndex(index: number | false) {
	let oldIdx: number | false = -1;
	let idx: number | false = index;
	while (idx !== oldIdx) {
		oldIdx = idx;
		idx = skipToLineEnd(idx);
		idx = skipBlockComment(idx);
		idx = skipSpaces(idx);
		idx = skipParens(idx);
	}
	idx = skipLineComment(idx);
	idx = skipParens(idx);
	idx = skipNewline(idx);
	idx = skipParens(idx);
	return idx !== false && hasNewline(idx);
}
export function hasNewline(index: number | false, backwards = false) {
	if (index === false) return false;
	const i = skipSpaces(index, backwards);
	return i !== false && i !== skipNewline(i, backwards);
}
function skipLineComment(index: number | false) {
	if (index === false) return false;
	const { commentSpans, originalText } = getContext().options;
	if (commentSpans.has(index) && originalText.charCodeAt(index + 1) === 47 /** "/" */)
		return skipEverythingButNewLine(commentSpans.get(index)!);
	return index;
}
function skipBlockComment(index: number | false) {
	if (index === false) return false;
	const { commentSpans, originalText } = getContext().options;
	if (commentSpans.has(index) && originalText.charCodeAt(index + 1) === 42 /** "*" */) return commentSpans.get(index)!;
	return index;
}
const [skipSpaces, skipToLineEnd, skipEverythingButNewLine] = [/[ \t]/, /[,; \t]/, /[^\r\n]/].map(function (re) {
	return function (index: number | false, backwards = false) {
		if (index === false) return false;
		const { originalText: text } = getContext().options;
		let cursor = index;
		while (cursor >= 0 && cursor < text.length) {
			if (re.test(text.charAt(cursor))) backwards ? cursor-- : cursor++;
			else return cursor;
		}
		return cursor === -1 || cursor === text.length ? cursor : false;
	};
});

function skipNewline(index: number | false, backwards = false) {
	if (index === false) return false;
	const { originalText } = getContext().options;
	const atIndex = originalText.charCodeAt(index);
	if (backwards) {
		if (originalText.charCodeAt(index - 1) === 13 && atIndex === 10) return index - 2;
		if (atIndex === 10) return index - 1;
	} else {
		if (atIndex === 13 && originalText.charCodeAt(index + 1) === 10) return index + 2;
		if (atIndex === 10) return index + 1;
	}
	return index;
}

function skipParens(index: number | false, backwards = false) {
	return index;
	// if (index === false) return false;
	// const { parensPositions } = getContext().options;
	// while (parensPositions.has(index)) backwards ? index-- : index++;
	// return index;
}

export function getNextNonSpaceNonCommentCharacterIndex(node: Node) {
	return getNextNonSpaceNonCommentCharacterIndexWithStartIndex(end(node));
}
function getNextNonSpaceNonCommentCharacterIndexWithStartIndex(i: number) {
	let oldIdx = -1;
	let nextIdx = i;
	while (nextIdx !== oldIdx) {
		oldIdx = nextIdx;
		nextIdx = skipSpaces(nextIdx) as number;
		nextIdx = skipBlockComment(nextIdx) as number;
		nextIdx = skipLineComment(nextIdx) as number;
		nextIdx = skipNewline(nextIdx) as number;
		nextIdx = skipParens(nextIdx) as number;
	}
	return nextIdx;
}
export function getNextNonSpaceNonCommentCharacter(node: Node) {
	return getContext().options.originalText.charAt(getNextNonSpaceNonCommentCharacterIndex(node));
}

interface CommentContext {
	comment: AnyComment;
	precedingNode: Node | undefined;
	enclosingNode: Node | undefined;
	followingNode: Node | undefined;
	text: string;
	options: CustomOptions;
	ast: Node;
	isLastComment: boolean;
}

function handled(comment: AnyComment) {
	return "printed" in comment;
}
function handleCommon(ctx: CommentContext): boolean {
	{
		const { comment, precedingNode, enclosingNode, followingNode } = ctx;
		if (!enclosingNode) {
			ctx.enclosingNode = ctx.comment.loc.src.program;
		} else if (enclosingNode && is_NodeWithBodyOrCases(enclosingNode)) {
			const body = getBodyOrCases(enclosingNode);
			if (body) {
				if (is_ExpressionWithBodyOrCases(enclosingNode) && enclosingNode.label) {
					if (ctx.precedingNode === enclosingNode.label) {
						ctx.precedingNode = undefined;
					}
					if (followingNode === enclosingNode.label) {
						ctx.followingNode = undefined;
					}
				}
				if (comment.loc.isBefore(body)) {
					if (followingNode && body.loc.contains(followingNode)) {
						ctx.followingNode = undefined;
					}
					if (!ctx.precedingNode && !ctx.followingNode) {
						addLeadingComment(enclosingNode, comment);
						return true;
					}
				} else if (comment.loc.isAfter(body)) {
					if (precedingNode && body.loc.contains(precedingNode)) {
						ctx.precedingNode = undefined;
					}
					if (!ctx.precedingNode && !ctx.followingNode) {
						addTrailingComment(enclosingNode, comment);
						return true;
					}
				} else if (body.loc.contains(comment)) {
					if (precedingNode && !body.loc.contains(precedingNode)) {
						ctx.precedingNode = undefined;
					}
					if (followingNode && !body.loc.contains(followingNode)) {
						ctx.followingNode = undefined;
					}
				}
			}
		}
	}
	for (const fn of [
		handleMixedInOuterAttributeComments,
		handleAttributeComments,
		handleDanglingComments,
		handleFunctionComments,
		handleMacroRuleComments,
		handleStructLiteralComments,
		handleVariableDeclaratorComments,
		handleIfBlockExpressionComments,
		handleMemberExpressionComments,
		handleStatementComments,
		handleFlowControlComments,
		handleBadComments,
	]) {
		fn(ctx);
		if (handled(ctx.comment)) {
			// console.log(ctx.comment.loc.url(), fn.name);
			return true;
		}
	}

	const { precedingNode, followingNode, comment } = ctx;

	if (isStartOfLine(comment)) {
		if (followingNode) {
			addLeadingComment(followingNode, comment);
		} else if (precedingNode) {
			addTrailingComment(precedingNode, comment);
		} else {
			exit.never(ctx);
		}
	} else if (isEndOfLine(comment)) {
		if (precedingNode) {
			addTrailingComment(precedingNode, comment);
		} else if (followingNode) {
			addLeadingComment(followingNode, comment);
		} else {
			exit.never(ctx);
		}
	} else {
		if (precedingNode && followingNode) {
			return false;
		} else if (precedingNode) {
			addTrailingComment(precedingNode, comment);
		} else if (followingNode) {
			addLeadingComment(followingNode, comment);
		} else {
			exit.never(ctx);
		}
	}
	return handled(ctx.comment);
}
export function handleOwnLineComment(ctx: CommentContext) {
	return handleCommon(ctx);
}
export function handleEndOfLineComment(ctx: CommentContext) {
	const { precedingNode, enclosingNode, comment } = ctx;
	if (
		// handleCallExpressionComments
		precedingNode &&
		enclosingNode &&
		is_CallExpression_or_CallLikeMacroInvocation(enclosingNode) &&
		enclosingNode.arguments.length > 0 &&
		precedingNode === (enclosingNode.typeArguments ? last_of(enclosingNode.typeArguments) : enclosingNode.callee)
	) {
		addLeadingComment(enclosingNode.arguments[0], comment);
		return true;
	} else if (
		// handlePropertyComments
		enclosingNode &&
		is_StructLiteralProperty(enclosingNode)
	) {
		addLeadingComment(enclosingNode, comment);
		return true;
	} else {
		return handleCommon(ctx);
	}
}

export function handleRemainingComment(ctx: CommentContext) {
	return handleCommon(ctx);
}

function handleStructLiteralComments({ enclosingNode, followingNode, comment }: CommentContext) {
	if (enclosingNode && is_StructLiteralPropertySpread(enclosingNode) && followingNode === enclosingNode.expression) {
		addLeadingComment(enclosingNode, comment);
	}
}

function handleVariableDeclaratorComments({ enclosingNode, followingNode, comment }: CommentContext) {
	if (
		enclosingNode &&
		(is_xVariableEqualishLike(enclosingNode) || is_ReassignmentNode(enclosingNode)) &&
		followingNode &&
		(is_BlockCommentKind(comment) ||
			nisAnyOf(followingNode, [
				NodeType.StructLiteral,
				NodeType.StructPattern,
				NodeType.TupleLiteral,
				NodeType.TypeTuple,
				NodeType.TuplePattern,
				NodeType.ArrayLiteral,
				NodeType.ArrayPattern,
				NodeType.SizedArrayLiteral,
				NodeType.TypeSizedArray,
			]))
	) {
		addLeadingComment(followingNode, comment);
	}
}

function handleMixedInOuterAttributeComments({ precedingNode, enclosingNode, followingNode, comment }: CommentContext) {
	if (enclosingNode && hasOuterAttributes(enclosingNode) && end(comment) <= ownStart(enclosingNode)) {
		if (isPrettierIgnoreComment(comment) || isPrettierIgnoreAttribute(comment)) {
			setPrettierIgnoreTarget(enclosingNode, comment);
		}
		if (isEndOfLine(comment)) {
			__DEV__: assert(!!precedingNode && is_Attribute(precedingNode), "", precedingNode);
			if (shouldPrintOuterAttributesAbove(enclosingNode)) {
				// #[attr] // comment
				// node
				addTrailingComment(precedingNode, comment);
			} else {
				// #[attr] /* comment */ node
				addLeadingComment(followingNode || enclosingNode, comment);
			}
		} else {
			// __DEV__: assert(isStartOfLine(comment));
			if (followingNode && end(followingNode) <= ownStart(enclosingNode)) {
				addLeadingComment(followingNode, comment);
			} else if (precedingNode && enclosingNode.loc.contains(precedingNode)) {
				addTrailingComment(precedingNode, comment);
			} else {
				addLeadingComment(enclosingNode, comment);
			}
		}
	}
}
function handleAttributeComments({ precedingNode, enclosingNode, followingNode, comment, ast }: CommentContext) {
	if (is_AttributeOrDocComment(comment)) {
		if (
			comment.inner &&
			enclosingNode &&
			is_FunctionDeclaration(enclosingNode) &&
			(!followingNode || !is_StatementNode(followingNode)) &&
			(!precedingNode || !is_StatementNode(precedingNode))
		) {
			if (enclosingNode.body) {
				if (canAttachCommentInLocArray(enclosingNode.body)) {
					addDanglingComment(enclosingNode, comment, DCM["body"]);
				} else {
					addLeadingComment(enclosingNode.body[0], comment);
				}
			} else {
				addLeadingComment(enclosingNode, comment);
			}
		} else {
			// if (comment.loc.url().startsWith("tests/samples/macro/attr.rs") && getContext().options.danglingAttributes.includes(comment)) {
			// 	// debugger;
			// 	console.log({
			// 		comment: comment.loc.url(),
			// 		precedingNode: precedingNode?.loc.url(),
			// 		enclosingNode: enclosingNode?.loc.url(),
			// 		followingNode: followingNode?.loc.url(),
			// 	});
			// }
			if (followingNode) {
				addLeadingComment(followingNode, comment);
			} else if (enclosingNode) {
				for (var key in DCM)
					if (key in enclosingNode) {
						addDanglingComment(enclosingNode, comment, key as DCM);
						return;
					}
			} else {
				addDanglingComment(ast, comment, DCM["body"]);
			}
		}
	}
}

function handleBadComments({ precedingNode, enclosingNode, followingNode, ast, comment }: CommentContext) {
	if (!enclosingNode) {
		// console.log(comment.loc.url());
		if (followingNode) {
			addLeadingComment(followingNode, comment);
		} else if (precedingNode) {
			addTrailingComment(precedingNode, comment);
		} else {
			addDanglingComment(enclosingNode || ast, comment, DCM["body"]);
		}
	} else if (!precedingNode && !followingNode) {
		if (enclosingNode && enclosingNode !== ast) {
			addLeadingComment(enclosingNode, comment);
		} else {
			addDanglingComment(ast, comment, DCM["body"]);
		}
	}
}
function is_ABI_Comment({ precedingNode, enclosingNode, comment }: CommentContext) {
	return (
		is_CommentOrDocComment(comment) &&
		((precedingNode && is_ExternSpecifier(precedingNode)) || (enclosingNode && is_ExternSpecifier(enclosingNode)))
	);
}
function handleFlowControlComments({ precedingNode, enclosingNode, followingNode, comment }: CommentContext) {
	if (enclosingNode && is_FlowControlExpression(enclosingNode)) {
		if (!precedingNode && (isOwnLine(comment) || isEndOfLine(comment)) && !followingNode) {
			addLeadingComment(enclosingNode, comment);
		}
	}
}
function handleFunctionComments(ctx: CommentContext) {
	const { precedingNode, enclosingNode, followingNode, comment } = ctx;
	if (enclosingNode && is_FunctionNode(enclosingNode)) {
		if (
			is_FunctionDeclaration(enclosingNode) &&
			((!is_ABI_Comment(ctx) && comment.loc.isBefore(enclosingNode.generics || enclosingNode.id)) ||
				(enclosingNode.generics && comment.loc.isBetween(enclosingNode.generics, enclosingNode.parameters)))
		) {
			addLeadingComment(enclosingNode, comment);
		} else if (
			!enclosingNode.returnType &&
			comment.loc.isBetween(
				enclosingNode.parameters,
				is_FunctionDeclaration(enclosingNode) ? enclosingNode.body! : enclosingNode.expression
			)
		) {
			if (is_FunctionDeclaration(enclosingNode)) {
				addCommentToBlock(enclosingNode, comment);
			} else {
				addLeadingComment(enclosingNode.expression, comment);
			}
		} else if (
			precedingNode && //
			enclosingNode.parameters.loc.contains(comment)
		) {
			if (precedingNode === getLastParameter(enclosingNode)) {
				addTrailingComment(precedingNode, comment);
			}
		} else if (
			followingNode &&
			isStartOfLine(comment) &&
			comment.loc.isAfter(enclosingNode.parameters) &&
			(!is_FunctionDeclaration(enclosingNode) || !enclosingNode.whereBounds || comment.loc.isAfter(enclosingNode.whereBounds!)) &&
			(!enclosingNode.returnType || comment.loc.isAfter(enclosingNode.returnType)) &&
			followingNode === (is_FunctionDeclaration(enclosingNode) ? enclosingNode.body?.[0] : enclosingNode.expression)
		) {
			addLeadingComment(followingNode, comment);
		}
	}
}
function handleMacroRuleComments(ctx: CommentContext) {
	const { precedingNode, enclosingNode, followingNode, comment } = ctx;
	if (enclosingNode && is_MacroRule(enclosingNode)) {
		if (enclosingNode.transform.loc.contains(comment)) {
			__DEV__: assert(enclosingNode.transform.length > 0);
			if (!precedingNode || !enclosingNode.transform.loc.contains(precedingNode)) {
				__DEV__: assert(!!followingNode && enclosingNode.transform.loc.contains(followingNode));
				addLeadingComment(followingNode, comment);
			}
		} else if (enclosingNode.match.loc.contains(comment)) {
			__DEV__: assert(enclosingNode.match.length > 0);
			if (!followingNode || !enclosingNode.match.loc.contains(followingNode)) {
				__DEV__: assert(!!precedingNode && enclosingNode.match.loc.contains(precedingNode));
				addTrailingComment(precedingNode!, comment);
			}
		}
	}
}

function handleStatementComments(ctx: CommentContext) {
	const { precedingNode, comment } = ctx;
	if (isEndOfLine(comment) && precedingNode && (is_StatementNode(precedingNode) || precedingNode.loc.sliceText().endsWith(";"))) {
		addTrailingComment(precedingNode, comment);
	}
}

function addCommentToBlock(block: NodeWithBodyOrCases, comment: AnyComment) {
	const body = getBodyOrCases(block);
	__DEV__: assert(!!body);
	if (body.length > 0) {
		addLeadingComment(body![0], comment);
	} else {
		addDanglingComment(block, comment, DCM["body"]);
	}
}

function handleIfBlockExpressionComments(ctx: CommentContext) {
	const { comment, enclosingNode } = ctx;
	if (enclosingNode && is_IfBlockExpression(enclosingNode)) {
		const { condition, body, else: else_ } = enclosingNode;
		if (comment.loc.isBefore(condition)) {
			addLeadingComment(condition, comment);
		} else if (comment.loc.isBetween(condition, body)) {
			addTrailingComment(condition, comment);
		} else if (else_ && comment.loc.isBetween(body, else_)) {
			if (is_IfBlockExpression(else_)) {
				addLeadingComment(else_.condition, comment);
			} else {
				addCommentToBlock(else_, comment);
			}
		}
	}
}

function handleMemberExpressionComments({ comment, precedingNode, enclosingNode }: CommentContext) {
	if (enclosingNode && is_MemberAccessLike(enclosingNode)) {
		if (isStartOfLine(comment) || !precedingNode) addLeadingComment(enclosingNode, comment);
		else addTrailingComment(precedingNode, comment);
		return true;
	}

	return false;
}

function handleDanglingComments({ comment, enclosingNode }: CommentContext) {
	if (enclosingNode) {
		for (var key in DCM) {
			if (key in enclosingNode) {
				var arr: LocArray = enclosingNode[key];
				if (is_LocArray(arr) && canAttachCommentInLocArray(arr) && arr.loc.contains(comment)) {
					addDanglingComment(enclosingNode, comment, key as DCM);
					return;
				}
			}
		}
	}
}

function canAttachCommentInLocArray(arr: LocArray) {
	return arr.length === 0 || arr.every((node) => !canAttachComment(node));
}

function isOwnLine(comment: AnyComment) {
	return isStartOfLine(comment) && hasBreaklineAfter(comment);
}
function isStartOfLine(comment: AnyComment) {
	return comment.placement === "ownLine";
}
function isEndOfLine(comment: AnyComment) {
	return comment.placement === "endOfLine";
}
export function isDangling(comment: AnyComment) {
	__DEV__: assert(handled(comment));
	return !comment.leading && !comment.trailing;
}
export function isLeading(comment: AnyComment) {
	__DEV__: assert(handled(comment));
	return comment.leading && !comment.trailing;
}
export function isTrailing(comment: AnyComment) {
	__DEV__: assert(handled(comment));
	return !comment.leading && comment.trailing;
}

export function print_comment(comment: CommentOrDocComment) {
	__DEV__: Narrow<PrettierCommentInfo>(comment);

	const doc = is_BlockCommentNode(comment)
		? isIndentableBlockComment(comment.value)
			? [
					(!handled(comment) || isTrailing(comment)) && !hasBreaklineBefore(comment) ? hardline : "",
					getCommentStart(comment),
					...comment.value.split(/\n/g).map((line, i, a) =>
						i === 0 //
							? [line.trimEnd(), hardline]
							: !iLast(i, a)
							? [" " + line.trim(), hardline]
							: " " + line.trimStart()
					),
					"*/",
			  ]
			: [
					getCommentStart(comment), //
					join(literalline, comment.value.split(/\n/g)),
					"*/",
			  ]
		: [getCommentStart(comment), comment.value.trimEnd()];

	return handled(comment) && isDangling(comment) //
		? [doc, getPostLeadingComment(comment)]
		: doc;

	function getCommentStart(comment: CommentOrDocComment) {
		return is_Comment(comment)
			? is_BlockCommentKind(comment)
				? "/*"
				: "//"
			: is_BlockCommentKind(comment)
			? isInner(comment)
				? "/*!"
				: "/**"
			: isInner(comment)
			? "//!"
			: "///";
	}
	function isIndentableBlockComment(value: string) {
		const lines = `*${value}*`.split(/\n/g);
		return lines.length > 1 && lines.every((line) => /^\s*\*/.test(line));
	}
}


================================================
FILE: src/format/complexity.ts
================================================
import {
	ForLtParametersBody,
	FunctionSpread,
	GenericParameterDeclaration,
	MaybeGenericArgsTarget,
	MissingNode,
	Node,
	NodeType,
	TypeBound,
	TypeBoundsConstaint,
	TypeCallArgument,
	TypeNamespaceTargetNoSelector,
	TypeNode,
} from "jinx-rust";
import {
	getAstPath,
	getOwnChildAstPath,
	is_BareTypeTraitBound,
	is_FunctionSpread,
	is_LetScrutinee,
	is_Literal,
	is_MissingNode,
	is_TypeBoundsStandaloneNode,
	is_TypeFunctionNode,
	is_TypeNode,
	is_VariableDeclarationNode,
} from "jinx-rust/utils";
import { exit, has_key_defined, last_of, spliceAll } from "../utils/common";
import { canBreak } from "./external";
import { getContext, getNode, getOptions, getPrintFn } from "./plugin";

let DEPTH = 0;
const ANCESTRY: Node[] = [];
const LONE_SHORT_ARGUMENT_THRESHOLD_RATE = 0.25;

export function withCheckContext<R>(fn: () => R): R {
	if (0 === DEPTH) {
		return fn();
	} else {
		DEPTH = 0;
		const prev = spliceAll(ANCESTRY);
		try {
			return fn();
		} finally {
			DEPTH = ANCESTRY.push(...prev);
		}
	}
}

export function is_short(str: string) {
	return str.length <= LONE_SHORT_ARGUMENT_THRESHOLD_RATE * getOptions().printWidth;
}
function print(target: Node) {
	const current = getNode();
	const keys: (string | number)[] = [...getAstPath(ANCESTRY[0], getNode())];
	for (let i = 1; i < ANCESTRY.length; i++) keys.push(...getOwnChildAstPath(ANCESTRY[i - 1], ANCESTRY[i]));
	keys.push(...getOwnChildAstPath(last_of(ANCESTRY), target));
	try {
		return getContext().path.call(() => getPrintFn(target)(), ...keys);
	} catch (e) {
		console.log({ current, target, keys, ANCESTRY });
		throw e;
	}
}

function IsSimpleFunction<T extends Node>(fn: (node: T) => boolean): (node: T) => boolean {
	return function (node: T) {
		if (0 !== DEPTH && node === ANCESTRY[DEPTH - 1]) {
			return fn(node);
		}

		if (DEPTH >= 2) {
			return isShortBasic(node);
		}

		try {
			return fn((ANCESTRY[DEPTH++] = node) as any);
		} finally {
			ANCESTRY.length = --DEPTH;
		}
	} as any;
}

function HasComplexFunction<T extends Node>(fn: (node: T) => boolean): (node: T) => boolean {
	return function (node: T) {
		if (0 !== DEPTH && node === ANCESTRY[DEPTH - 1]) {
			return fn(node);
		}

		if (DEPTH >= 2) {
			return !isShortBasic(node);
		}

		try {
			return fn((ANCESTRY[DEPTH++] = node) as any);
		} finally {
			ANCESTRY.length = --DEPTH;
		}
	} as any;
}

const isShortBasic = (node: Node) => {
	switch (node.nodeType) {
		case NodeType.MissingNode:
			return true;
		case NodeType.Identifier:
		case NodeType.Index:
		case NodeType.LtIdentifier:
		case NodeType.LbIdentifier:
		case NodeType.McIdentifier:
			return is_short(node.name);
		case NodeType.Literal:
			return is_short(node.value) && !/\n/.test(node.value);
	}
	return false;
};

export const isSimpleType = IsSimpleFunction<FunctionSpread | TypeNode | MissingNode>((node): boolean => {
	switch (node.nodeType) {
		case NodeType.MissingNode:
		case NodeType.FunctionSpread:
			return true;
		case NodeType.MacroInvocation:
			return false;
		case NodeType.Identifier:
		case NodeType.TypeNever:
		case NodeType.TypeInferred:
			return true;
		case NodeType.TypePath:
			return isShortBasic(node.segment) && (!node.namespace || isSimpleType(node.namespace));
		case NodeType.TypeCall:
			return isSimpleType(node.typeCallee) && !hasComplexTypeArguments(node);
		case NodeType.ExpressionTypeSelector:
			return isSimpleType(node.typeTarget) && (!node.typeExpression || isSimpleType(node.typeExpression));
		case NodeType.TypeDynBounds:
			return !hasComplexTypeBounds(node);
		case NodeType.TypeImplBounds:
			return !hasComplexTypeBounds(node);
		case NodeType.TypeFnPointer: {
			const param = node.parameters[0];
			return (
				(!node.extern || !node.extern.abi || isShortBasic(node.extern.abi)) &&
				!hasComplexLtParameters(node) &&
				(node.parameters.length === 0 ||
					(node.parameters.length === 1 &&
						(is_FunctionSpread(param) ||
							(!is_TypeFunctionNode(param.typeAnnotation) && isSimpleType(param.typeAnnotation))))) &&
				(!node.returnType || isSimpleType(node.returnType))
			);
		}
		case NodeType.TypeFunction:
			return isSimpleType(node.callee) && node.parameters.every(isSimpleType) && (!node.returnType || isSimpleType(node.returnType));
		case NodeType.TypeSizedArray:
			return isSimpleType(node.typeExpression) && isShortBasic(node.sizeExpression);
		case NodeType.TypeSlice:
			return isSimpleType(node.typeExpression);
		case NodeType.TypeTuple:
			return node.items.length === 0 || (node.items.length === 1 && isSimpleType(node.items[0]));
		case NodeType.TypeReference:
		case NodeType.TypeDereferenceMut:
		case NodeType.TypeDereferenceConst:
		case NodeType.TypeParenthesized:
			return isSimpleType(node.typeExpression);
		default:
			__DEV__: exit.never(node);
			return false;
	}
});

export const hasComplexTypeBounds = HasComplexFunction<Extract<Node, TypeBoundsConstaint>>((node) => {
	return !!node.typeBounds && node.typeBounds.length > 1 && !node.typeBounds.every(isSimpleTypeBound);
});

export const isSimpleTypeBound = (node: TypeBound): boolean => {
	switch (node.nodeType) {
		case NodeType.TypeParenthesized:
			return isSimpleTypeBound(node.typeExpression);
		// #Lifetime
		case NodeType.LtIdentifier:
		case NodeType.LtElided:
		case NodeType.LtStatic:
			return true;
		case NodeType.TypeTraitBound:
			return is_BareTypeTraitBound(node) && isSimpleTypeNamespaceTargetNoSelector(node.typeExpression);
		default:
			__DEV__: exit.never(node);
			return false;
	}
	function isSimpleTypeNamespaceTargetNoSelector(node: TypeNamespaceTargetNoSelector): boolean {
		switch (node.nodeType) {
			case NodeType.Identifier:
				return true;
			case NodeType.TypePath:
				return undefined === node.namespace || isSimpleTypeNamespaceTargetNoSelector(node.namespace);
			case NodeType.TypeCall:
				return false;
			case NodeType.TypeFunction:
				return isSimpleTypeNamespaceTargetNoSelector(node.callee) && node.parameters.length === 0 && !node.returnType;
			default:
				__DEV__: exit.never(node);
				return false;
		}
	}
};

const isSimpleTypeArgument = IsSimpleFunction<TypeCallArgument>((node) => {
	if (is_TypeNode(node)) {
		return isSimpleType(node);
	}
	switch (node.nodeType) {
		// #Lifetime
		case NodeType.LtIdentifier:
		case NodeType.LtElided:
		case NodeType.LtStatic:
		case NodeType.Literal:
			return true;
		case NodeType.MinusExpression:
			return is_Literal(node.expression);
		case NodeType.BlockExpression:
			return false; //willBreak(getPrintFn(node)("body"));
		case NodeType.TypeCallNamedArgument:
			return isSimpleType(node.typeExpression);
		case NodeType.TypeCallNamedBound:
			return isSimpleType(node.typeTarget) && !hasComplexTypeBounds(node);
		default:
			__DEV__: exit.never(node);
			return false;
	}
});

export const hasComplexTypeArguments = HasComplexFunction<Extract<Node, MaybeGenericArgsTarget>>((node) =>
	!node.typeArguments || node.typeArguments.length === 0
		? false
		: node.typeArguments.length === 1
		? (() => {
				const arg = node.typeArguments[0];
				return is_TypeBoundsStandaloneNode(arg) || canBreak(print(arg));
		  })()
		: true
);

export const hasComplexLtParameters = HasComplexFunction<Extract<Node, ForLtParametersBody>>((node) => {
	const ltParameters = node.ltParameters;
	if (!ltParameters || ltParameters.length === 0) {
		return false;
	}
	if (ltParameters.length === 1) {
		const arg = ltParameters[0];
		if (arg.ltBounds && arg.ltBounds.length > 1) {
			return true;
		}

		return false;
	}
	return true;
});

export const isShortGenericParameterDeclaration = IsSimpleFunction<GenericParameterDeclaration>((node) => {
	switch (node.nodeType) {
		case NodeType.GenericTypeParameterDeclaration:
			return !node.typeBounds && !node.typeDefault;
		case NodeType.ConstTypeParameterDeclaration:
			return (!node.typeAnnotation || is_MissingNode(node)) && !node.typeDefault;
		case NodeType.GenericLtParameterDeclaration:
			return !node.ltBounds;
		default:
			exit.never();
	}
});

export const hasComplexGenerics = HasComplexFunction<Node>((node) => {
	return has_key_defined(node, "generics") && node.generics.length > 0 && !node.generics.every(isShortGenericParameterDeclaration);
});

export const hasComplexTypeAnnotation = HasComplexFunction<Node>((node) => {
	if (is_VariableDeclarationNode(node) && !is_LetScrutinee(node)) {
		const { typeAnnotation } = node;
		return !!typeAnnotation && !is_MissingNode(typeAnnotation) && !isSimpleType(typeAnnotation);
	} else {
		return false;
	}
});


================================================
FILE: src/format/core.ts
================================================
import {
	AndExpression,
	AttributeOrDocComment,
	BreakExpression,
	CallExpression,
	ClosureFunctionExpression,
	ComparisonExpression,
	DeclarationNode,
	DelimKind,
	EnumDeclaration,
	ExpressionBody,
	ExpressionNode,
	ExpressionPath,
	ExpressionWithBody,
	ForLtParametersBody,
	FunctionDeclaration,
	FunctionLike,
	FunctionNode,
	IfBlockExpression,
	ImplDeclaration,
	LeftRightExpression,
	LocArray,
	MacroDeclaration,
	MacroGroup,
	MacroInlineRuleDeclaration,
	MacroMatchSegment,
	MacroRuleDeclaration,
	MacroRulesDeclaration,
	MatchExpression,
	MatchExpressionCase,
	MaybeBlockBody,
	MaybeGenericArgsTarget,
	MaybeHasLtBounds,
	MaybeReturnTypeConstraint,
	MaybeTypeAnnotationTarget,
	MemberExpression,
	NegativeImplDeclaration,
	Node,
	NodeType,
	NodeWithBodyOrCases,
	NodeWithCondition,
	ObjectNode,
	OperationExpression,
	OrExpression,
	PathNode,
	PatternBody,
	PatternNode,
	PunctuationToken,
	RangeNode,
	RestPattern,
	ReturnExpression,
	StructDeclaration,
	StructLiteral,
	StructLiteralPropertySpread,
	StructLiteralRestUnassigned,
	TK,
	TraitAliasDeclaration,
	TraitDeclaration,
	TupleStructDeclaration,
	TypeAliasDeclaration,
	TypeBoundsConstaint,
	TypeFunctionNode,
	UnaryExpression,
	UnionDeclaration,
	UnionPattern,
	WhileBlockExpression,
	YieldExpression,
} from "jinx-rust";
import {
	DelimChars,
	end,
	getBodyOrCases,
	getDelimChars,
	getParameters,
	hasAttributes,
	hasCondition,
	hasItems,
	hasLetScrutineeCondition,
	hasParameters,
	hasProperties,
	hasSelfParameter,
	hasSemiNoBody,
	hasSemiNoProperties,
	hasTypeBounds,
	is_ArrayOrTupleLiteral,
	is_Attribute,
	is_ClosureBlock,
	is_ClosureFunctionExpression,
	is_DelimGroup,
	is_ElseBlock,
	is_EnumMemberStructDeclaration,
	is_ExpressionAsTypeCast,
	is_ExpressionNode,
	is_ExpressionPath,
	is_ExpressionStatement,
	is_ExpressionTypeCast,
	is_ExpressionWithBodyOrCases,
	is_FlowControlExpression,
	is_FunctionDeclaration,
	is_FunctionParameterDeclaration,
	is_GenericParameterDeclaration,
	is_Identifier,
	is_IdentifierOrIndex,
	is_IfBlockExpression,
	is_ImplDeclarationNode,
	is_LetScrutinee,
	is_LineCommentNode,
	is_Literal,
	is_LiteralBooleanLike,
	is_LiteralNumberLike,
	is_LiteralStringLike,
	is_LogicalExpression,
	is_MacroGroup,
	is_MacroInlineRuleDeclaration,
	is_MacroInvocation,
	is_MacroParameterDeclaration,
	is_MatchExpression,
	is_MatchExpressionCase,
	is_MemberExpression,
	is_MinusExpression,
	is_MissingNode,
	is_NodeWithBodyOrCases,
	is_OrExpression,
	is_PostfixExpression,
	is_Program,
	is_PunctuationToken,
	is_RangeLiteral,
	is_RangePattern,
	is_ReassignmentExpression,
	is_ReassignmentNode,
	is_RestPattern,
	is_SourceFile,
	is_StructDeclaration,
	is_StructLiteral,
	is_StructLiteralProperty,
	is_StructLiteralPropertySpread,
	is_StructPattern,
	is_StructPatternPropertyDestructured,
	is_StructProperty,
	is_TupleLiteral,
	is_TupleNode,
	is_TuplePattern,
	is_TupleStructDeclaration,
	is_TypeCallNamedArgument,
	is_TypeTuple,
	is_UnaryExpression,
	is_UnionDeclaration,
	is_UnwrapExpression,
	is_VariableDeclarationNode,
	ownStart,
	start,
} from "jinx-rust/utils";
import { BlockLikeMacroInvocation, CallLikeMacroInvocation, is_CallExpression_or_CallLikeMacroInvocation } from "../transform";
import { AssertTypesEq, Identity, Map_get, Narrow, assert, exit, find_last, flat, iLast, last_of, spread } from "../utils/common";
import {
	CF,
	getFirstComment,
	getNextNonSpaceNonCommentCharacterIndex,
	hasBreaklineBefore,
	hasComment,
	hasNewline,
	hasNewlineInRange,
	isNextLineEmpty,
	isNextLineEmptyAfterIndex,
	isPreviousLineEmpty,
	printCommentsSeparately,
	printDanglingComments,
	withComments,
} from "./comments";
import {
	hasComplexGenerics,
	hasComplexLtParameters,
	hasComplexTypeAnnotation,
	hasComplexTypeArguments,
	hasComplexTypeBounds,
	isShortGenericParameterDeclaration,
	is_short,
} from "./complexity";
import {
	AstPath,
	DCM,
	Doc,
	align,
	breakParent,
	canBreak,
	cleanDoc,
	conditionalGroup,
	dedentToRoot,
	fill,
	getDocParts,
	group,
	hardline,
	ifBreak,
	indent,
	indentIfBreak,
	isConcat,
	join,
	label,
	line,
	lineSuffixBoundary,
	removeLines,
	softline,
	willBreak,
} from "./external";
import {
	f,
	getContext,
	getGrandParentNode,
	getNode,
	getOptions,
	getParentNode,
	getPrintFn,
	is_printing_macro,
	pathCall,
	pathCallEach,
	print,
} from "./plugin";
import { canInlineBlockBody, emptyContent, needsParens, shouldFlatten } from "./styling";

export function isNoopExpressionStatement(node: Node) {
	return is_ExpressionStatement(node) && undefined === node.expression && !hasAttributes(node) && !hasComment(node);
}

export function getLastNotNoopExpressionStatement(parent: MaybeBlockBody) {
	return parent.body && find_last(parent.body, (stmt) => !isNoopExpressionStatement(stmt));
}

export function is_xVariableEqualishLike(node: Node) {
	switch (node.nodeType) {
		case NodeType.LetScrutinee:
		case NodeType.LetVariableDeclaration:
		case NodeType.ConstVariableDeclaration:
		case NodeType.StaticVariableDeclaration:
		case NodeType.TypeAliasDeclaration:
		case NodeType.TraitAliasDeclaration:
			return true;
		default:
			return false;
	}
}

export function is_BinaryishExpression(node: Node): node is OrExpression | AndExpression | OperationExpression | ComparisonExpression {
	switch (node.nodeType) {
		case NodeType.OrExpression:
		case NodeType.AndExpression:
		case NodeType.OperationExpression:
		case NodeType.ComparisonExpression:
			return true;
		default:
			return false;
	}
}

export type StructSpread = StructLiteralPropertySpread | StructLiteralRestUnassigned | RestPattern;
export function is_StructSpread(node: Node): node is StructSpread {
	switch (node.nodeType) {
		case NodeType.StructLiteralPropertySpread:
		case NodeType.StructLiteralRestUnassigned:
		case NodeType.RestPattern:
			return true;
		default:
			return false;
	}
}

type ArrayLikeNode = Exclude<Extract<Node, { items: Node[] }>, UnionPattern>;
function isConciselyPrintedArray(node: ArrayLikeNode) {
	return (
		node.items.length > 1 &&
		(node.items as Node[]).every(
			(element) =>
				(is_LiteralNumberLike(element) ||
					(is_MinusExpression(element) && is_LiteralNumberLike(element.expression) && !hasComment(element.expression))) &&
				!hasComment(element, CF.Trailing | CF.Line, (comment) => !hasBreaklineBefore(comment))
		)
	);
}
export function printCommentsInsideEmptyArray(path: AstPath<Node>) {
	const node = path.getValue();
	if (hasComment(node, CF.Dangling)) {
		return [printDanglingComments(node, false, DCM["items"]), softline];
	} else {
		return "";
	}
}

export function printNumber(rawNumber: string) {
	return rawNumber
		.toLowerCase()
		.replace(/^([\d.]+e)(?:\+|(-))?0*(\d)/, "$1$2$3")
		.replace(/^(\d+)e[+-]?0+$/, "$1.0")
		.replace(/^([\d.]+)e[+-]?0+$/, "$1")
		.replace(/\.(\d+?)0+(?=e|$)/, ".$1")
		.replace(/\.(?=e|$)/, ".0");
}

export function printOnOwnLine(node: Node, printed: Doc) {
	return [printed, maybeEmptyLine(node)];
}
export function maybeEmptyLine(node: Node) {
	return isNextLineEmpty(node) ? [hardline, hardline] : hardline;
}

export function printBodyOrCases<T extends NodeWithBodyOrCases | BlockLikeMacroInvocation>(print: print<T>, node: T) {
	// Note: Inner Attributes are inserted into body/cases (see "./transform.ts")
	// Example: { #[OUTER] #![INNER] 0 }; body: ["#[OUTER] 0", "#![INNER]"]
	const p: { node: Node; doc: Doc }[] = [];
	if (is_MatchExpression(node)) {
		__DEV__: Narrow<print<MatchExpression>>(print);
		pathCallEach(node as Extract<Node, MatchExpression>, "cases", (mCase) => {
			p.push({
				node: mCase,
				doc: is_MatchExpressionCase(mCase) && !is_ExpressionWithBodyOrCases(mCase.expression) ? [print(), ","] : print(),
			});
		});
	} else {
		__DEV__: Narrow<Extract<Node, MaybeBlockBody>>(node);
		pathCallEach(node as Extract<Node, MaybeBlockBody>, "body", (stmt) => {
			if (!isNoopExpressionStatement(stmt)) {
				p.push({ node: stmt, doc: print() });
			}
		});
	}

	const printed: Doc[] = bumpInnerAttributes(p).map(({ doc, node }, i, a) =>
		iLast(i, a) ? group(doc) : printOnOwnLine(node, group(doc))
	);

	const comments = printDanglingCommentsForInline(node, DCM["body"]);
	if (comments) printed.push(comments);
	const ccomments = printDanglingCommentsForInline(node, DCM["cases"]);
	if (ccomments) printed.push(ccomments);

	if (is_Program(node) && is_SourceFile(getParentNode(node)!) && printed.length > 0 && !comments) {
		printed.push(hardline);
	}

	return printed;

	function bumpInnerAttributes(arr: { node: Node; doc: Doc }[]) {
		return arr.sort((a, b) => ownStart(a.node) - ownStart(b.node));
	}
}

export function printMacroRules<T extends MacroDeclaration | MacroRulesDeclaration>(print: print<T>, node: T) {
	return !Array.isArray(node.rules)
		? print("rules")
		: node.rules.length > 0
		? [" {", indent([hardline, ...print.join("rules", (rule) => maybeEmptyLine(rule))]), hardline, "}"]
		: [" {", printDanglingCommentsForInline(node, DCM["rules"]) || emptyContent(node), "}"];
}
function is_unary_token(item: MacroMatchSegment | undefined) {
	switch (item && is_PunctuationToken(item) ? item.tk : TK.None) {
		case TK["-"]:
		case TK["*"]:
		case TK["&"]:
		case TK["#"]:
		case TK["!"]:
		case TK["~"]:
			return true;
		case TK["?"]:
			return !/\s/.test(getOptions().originalText.charAt(end(item!)));
		default:
			return false;
	}
}
function can_unary(node: MacroMatchSegment) {
	return (!is_PunctuationToken(node) || is_unary_token(node)) && (!is_MacroGroup(node) || is_optional_unary(node));
}
function is_optional_token(item: MacroMatchSegment | undefined): item is MacroGroup<MacroMatchSegment> & { segments: [PunctuationToken] } {
	return !!item && is_MacroGroup(item) && item.kind === "?" && item.segments.length === 1 && is_PunctuationToken(item.segments[0]);
}
function is_optional_unary(item: MacroMatchSegment | undefined) {
	return is_optional_token(item) && is_unary_token(item.segments[0]);
}

function is_optional_segment(item: Node): item is MacroGroup {
	return is_MacroGroup(item) && item.kind === "?";
}

export function printRuleMatch<T extends MacroRuleDeclaration | MacroInlineRuleDeclaration>(print: print<T>, rule: T) {
	// "", ".", "&&", "||", "=", "+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "==", "!=", ">", ">=", "<", "<=", "+=", "-=", "*=", "/=",
	// "%=", "&=", "|=", "^=", "<<=", ">>=", "$", "@", "_", "..", "...", "..=", ",", ";", ":", "::", "#", "?", "!", "=>", "->", "~"

	return print_map(rule, "match");

	type ArrProps<T extends {}> = { [K in keyof T]: NonNullable<T[K]> extends readonly any[] ? T[K] & unknown[] : never };
	function print_map<T extends Node, K extends keyof ArrProps<T> & keyof typeof DCM>(node: T, property: K) {
		__DEV__: assert(property in DCM);
		const arr = node[property as any] as LocArray<MacroMatchSegment>;
		const shouldHug = should_hug(arr);
		const dline =
			arr.dk === DelimKind["{}"] ? line : shouldHug ? "" /*  : is_MacroGroup(node) && node.kind !== "?" ? hardline */ : softline;
		const isParamsLike = is_params_like(arr);
		const shouldBreak = should_break(arr);
		const d = getDelimChars(arr);
		if (arr.length === 0) return [d.left, printDanglingCommentsForInline(node, DCM[property]), d.right];
		const printed = flat(print.map_join(property as any, print_item, join_item));

		// const printed = flat(print.map_join(property as any, print_item, join_item))
		// 	.reduce(
		// 		(arr, doc, i, a) => {
		// 			last_of(arr).push(doc);
		// 			if (doc === line && (a[i - 1] === "," || a[i - 1] === ";")) arr.push([]);
		// 			return arr;
		// 		},
		// 		[[]] as Doc[][]
		// 	)
		// 	.map((grp) => group(grp));

		return group([d.left, !dline ? printed : [indent([dline, printed]), dline], d.right], {
			shouldBreak,
			id: getMacroGroupId(node),
		});

		function should_hug(arr: LocArray<MacroMatchSegment>) {
			if (node === (rule as any)) return false;
			let has_nonToken = false;
			return arr.every((item) => !is_MacroGroup(item) && (is_PunctuationToken(item) || has_nonToken !== (has_nonToken = true)));
		}

		function should_break(arr: LocArray<MacroMatchSegment>) {
			let has_decl = false;
			return arr.some(
				(item, i, a) =>
					(is_match_any(item) && arr.length !== 1) ||
					(!iLast(i, a) && isDeclStart(item, a[i + 1]) && has_decl === (has_decl = true))
			);
		}

		function print_item(item: MacroMatchSegment, index: number, arr: MacroMatchSegment[]) {
			switch (item.nodeType) {
				case NodeType.Identifier:
				case NodeType.LtIdentifier:
				case NodeType.Literal:
				case NodeType.PunctuationToken:
				case NodeType.MacroParameterDeclaration:
					return print();
				case NodeType.MacroGroup:
					Narrow<print<MacroGroup>>(print);
					return printComments(["$", print_map(item, "segments"), print("sep"), item.kind]);
				case NodeType.DelimGroup:
					return printComments(print_map(item, "segments"));
			}
			__DEV__: exit.never();

			function printComments(doc: Doc) {
				const printed = withComments(item, doc);
				const comment = getFirstComment(item, CF.Leading | CF.Line);
				return comment && index !== 0
					? isPreviousLineEmpty(comment) &&
					  typeof join_item(arr[index - 1], item, index === 1 ? undefined : arr[index - 2]) === "string"
						? [hardline, hardline, printed]
						: [hardline, printed]
					: printed;
			}
		}

		function is_params_like(arr: MacroMatchSegment[]) {
			return arr.some(function isComma(item) {
				switch (item.nodeType) {
					case NodeType.PunctuationToken:
						return TK[","] === item.tk;
					case NodeType.MacroGroup:
						return (!!item.sep && isComma(item.sep)) || is_params_like(item.segments);
				}
			});
		}

		function join_item(item: MacroMatchSegment, next: MacroMatchSegment, prev: MacroMatchSegment | undefined) {
			if (is_PunctuationToken(item)) {
				switch (item.tk) {
					case TK[","]:
					case TK[";"]:
						return line;
					case TK["::"]:
					case TK[".."]:
					case TK["..."]:
					case TK["."]:
					case TK["#"]:
						return "";
					case TK["!"]:
						if (prev && is_ident(prev) && is_DelimGroup(next)) {
							return next.segments.dk === DelimKind["{}"] ? " " : "";
						}
						break;
					case TK["@"]:
						return is_ident(next) && (!prev || is_MacroGroup(prev) || is_DelimGroup(prev)) ? "" : " ";
				}

				return is_unary_token(item) && //
					(!prev || !is_ident(prev)) &&
					can_unary(next)
					? ""
					: " ";
			}

			switch (is_PunctuationToken(next) ? next.tk : TK.None) {
				case TK[","]:
				case TK[";"]:
				case TK[":"]:
				case TK["::"]:
				case TK[".."]:
				case TK["..."]:
				case TK["."]:
					return "";
				case TK["!"]:
					if (is_ident(item)) {
						return "";
					}
			}

			if (is_match_any(item)) {
				return line;
			}

			{
				const sep_tk = is_MacroGroup(item) && item.sep && is_PunctuationToken(item.sep) ? item.sep.tk : TK.None;
				switch (sep_tk) {
					case TK["::"]:
					case TK["."]:
						return ""; // $(...)::* | $(...).*
					case TK[","]:
					case TK[";"]:
						return sep_tk === maybe_tk(next) ? ifBreak(line, " ", { groupId: getMacroGroupId(item) }) : line;
				}
			}

			if (is_optional_token(item)) {
				switch (item.segments[0].tk) {
					case TK["+"]:
					case TK["|"]:
						return " ";
					case TK["::"]:
						return "";
				}
				if (is_unary_token(item.segments[0])) {
					return "";
				}
			}

			if (is_DelimGroup(item) || is_MacroGroup(item)) {
				if (item.segments.dk === DelimKind["{}"]) {
					return line;
				}

				if (is_MacroGroup(item) && item.segments.length === 2) {
					const { 0: left, 1: right } = item.segments;
					if (is_PunctuationToken(left) && is_DelimGroup(right) && left.tk === TK["#"] && right.segments.dk === DelimKind["[]"]) {
						return hardline;
					}
				}
				return isParamsLike || is_tk(next) ? " " : line;
			}

			const next_1 = next !== last_of(arr) && arr[arr.indexOf(next) + 1];
			if (is_ident(item) && is_DelimGroup(next) && next.segments.dk === DelimKind["()"]) {
				if (!next_1 || !is_match_any(next_1)) {
					return "";
				}
			}

			if (is_match_any(next) && (!is_DelimGroup(next) || (next_1 && is_match_any(next_1)))) {
				return line;
			}

			// if (is_ident(item) && !is_ident(next) && !(is_DelimGroup(next) && next.segments.dk === DelimKind["{}"])) {
			// 	const next_1 = arr[arr.indexOf(next) + 1];
			// 	if (next_1 && typeof join_item(next, next_1, item) === "object") {
			// 		return line;
			// 	}
			// }

			return " ";
		}
	}

	function is_ident(item: MacroMatchSegment) {
		switch (item.nodeType) {
			case NodeType.Identifier:
				return true;
			case NodeType.MacroParameterDeclaration:
				return item.ty.name === "ident";
			default:
				return false;
		}
	}
	function is_tk(item: MacroMatchSegment) {
		return is_PunctuationToken(item) || is_optional_token(item);
	}
	function maybe_tk(item: MacroMatchSegment) {
		switch (item.nodeType) {
			case NodeType.PunctuationToken:
				return item.tk;
			case NodeType.MacroGroup:
				return is_optional_token(item) ? item.segments[0].tk : TK.None;
			default:
				return TK.None;
		}
	}
	function isDeclStart(item: MacroMatchSegment, next: MacroMatchSegment) {
		if (is_Identifier(item)) {
			switch (item.name) {
				case "fn":
				case "mod":
				case "use":
				case "struct":
				case "trait":
				case "union":
				case "enum":
				case "impl":
				case "type":
				case "let":
				case "static":
				case "const":
					if (is_ident(next)) {
						return true;
					}
			}
		}
		return false;
	}
	function is_match_any(item: MacroMatchSegment) {
		return (
			!!item &&
			((is_MacroGroup(item) &&
				!item.sep &&
				(item.kind === "*" || item.kind === "+") &&
				item.segments.length === 1 &&
				is_MacroParameterDeclaration(item.segments[0]) &&
				item.segments[0].ty.name === "tt") ||
				(is_DelimGroup(item) && item.segments.length === 1 && is_match_any(item.segments[0])))
		);
	}
}

export function printRuleTransform<T extends MacroRuleDeclaration | MacroInlineRuleDeclaration>(
	print: print<T>,
	node: T,
	t: DelimChars = getDelimChars(node.transform)
) {
	const text = node.transform.loc.sliceText();
	const fline = is_MacroInlineRuleDeclaration(node) ? hardline : line;
	if (/^. *\n/.test(text)) {
		return [
			dedentToRoot([
				t.left,
				fline,
				text
					.slice(1, -1) //
					.replace(/^ *\n|\n\s*$/g, ""),
			]),
			fline,
			t.right,
		];
	} else if (/\n/.test(text) && node.transform.length === 1) {
		const segment = node.transform[0];
		if (is_DelimGroup(segment) || is_MacroGroup(segment)) {
			const inner = is_DelimGroup(segment)
				? getDelimChars(segment.segments)
				: { left: "$(", right: `)${segment.sep?.loc.getOwnText() ?? ""}${segment.kind}` };
			return [
				dedentToRoot([
					t.left,
					[
						indent(indent([fline, inner.left])),
						line,
						segment.segments.loc.sliceText(1, -1).replace(/^ *\n|\n\s*$/g, ""),
						indent(indent([line, inner.right])),
					],
				]),
				fline,
				t.right,
			];
		}
	}
	return text;

	return node.transform.length > 0
		? group([t.left, indent([line, print("transform")]), line, t.right])
		: [t.left, printDanglingCommentsForInline(node, DCM["transform"]), t.right];
}

function is_AssignmentOrVariableDeclarator(node: Node): boolean {
	return is_ReassignmentNode(node) || is_VariableDeclarationNode(node);
}
function hasLeadingOwnLineComment(node: Node): boolean {
	if (is_NodeWithBodyOrCases(node) && hasComment(node, CF.Leading, is_Attribute)) {
		return true;
	}
	return hasComment(
		node,
		CF.Leading,
		(comment) => hasNewline(end(comment)) && !getContext().options.danglingAttributes.includes(comment as any)
	);
}
function isComplexDestructuring(node: Node): boolean {
	if (is_ReassignmentExpression(node)) {
		const leftNode = node.left;
		return (
			is_StructLiteral(leftNode) && //
			leftNode.properties.length > 2 &&
			leftNode.properties.some((property) => is_StructLiteralProperty(property) || is_StructLiteralPropertySpread(property))
		);
	}
	if (is_VariableDeclarationNode(node) || is_MatchExpressionCase(node) || is_LetScrutinee(node)) {
		const leftNode = node.pattern;
		return (
			is_StructPattern(leftNode) && //
			leftNode.properties.length > 2 &&
			leftNode.properties.some((property) => is_StructPatternPropertyDestructured(property))
		);
	}
	return false;
}

// export function isShortWhereBoundDeclaration(node: WhereBoundDeclaration) {
// 	switch (node.nodeType) {
// 		case NodeType.WhereTypeBoundDeclaration:
// 			__DEV__: Narrow<WhereTypeBoundDeclaration>(node);
// 			return !node.ltParameters && is_Identifier(node.typeTarget);
// 		case NodeType.WhereLtBoundDeclaration:
// 			__DEV__: Narrow<WhereLtBoundDeclaration>(node);
// 			// return !node.typeAnnotation && !node.typeDefault;
// 		default:
// 			exit.never();
// 	}
// }

function isArrowFunctionVariableDeclarator(node: Node) {
	return is_VariableDeclarationNode(node) && node.expression && is_ClosureFunctionExpression(node.expression);
}
function isObjectPropertyWithShortKey(node: Node, keyDoc: Doc) {
	if (!is_StructProperty(node)) return false;
	keyDoc = cleanDoc(keyDoc);
	const MIN_OVERLAP_FOR_BREAK = 3;
	return typeof keyDoc === "string" && keyDoc.length < getContext().options.tabWidth + MIN_OVERLAP_FOR_BREAK;
}

function print_CallExpression_end(print: print<CallExpression>, node: CallExpression) {
	return [f`::${printTypeArguments(print, node)}`, printCallArguments(print, node)];
}

export function printCallExpression(print: print<CallExpression>, node: CallExpression) {
	if (shouldPrint_CallExpression_chain(node) && !pathCall(node, "callee", (node) => needsParens(node))) {
		return printMemberChain(print, node);
	}

	const contents = [print("callee"), ...print_CallExpression_end(print, node)];

	if (is_CallExpression_or_CallLikeMacroInvocation(node.callee)) {
		return group(contents);
	}

	return contents;
}

export function printTypeAnnotation<T extends Extract<Node, MaybeTypeAnnotationTarget<any>>>(print: print<T>, node: T) {
	return node.typeAnnotation && !is_MissingNode(node.typeAnnotation) ? [": ", print("typeAnnotation")] : "";
}
export function printAnnotatedPattern<T extends Extract<Node, MaybeTypeAnnotationTarget<any> & PatternBody>>(print: print<T>, node: T) {
	return [print("pattern"), printTypeAnnotation(print, node)];
}

function isLoneShortArgument(node: Node) {
	if (hasComment(node)) {
		return false;
	}

	if ((is_Identifier(node) && is_short(node.name)) || (is_LiteralNumberLike(node) && !hasComment(node))) {
		return true;
	}

	if (is_LiteralStringLike(node)) {
		return is_short(node.value) && !node.value.includes("\n");
	}

	return is_LiteralBooleanLike(node);
}

// prettier-ignore
const toLayout = ["break-after-operator", "never-break-after-operator", "fluid", "break-lhs", "chain", "chain-tail", "chain-tail-arrow-chain", "only-left"];
const enum Layout {
	"break-after-operator",
	"never-break-after-operator",
	"fluid",
	"break-lhs",
	"chain",
	"chain-tail",
	"chain-tail-arrow-chain",
	"only-left",
}

export function printMemberExpression(print: print<MemberExpression>, node: MemberExpression) {
	const objectDoc = print("expression");
	const lookupDoc = printMemberLookup(print, node);

	const shouldInline = shouldInlineMemberExpression(node, objectDoc);

	return label((objectDoc as any).label === "member-chain" ? "member-chain" : "member", [
		objectDoc,
		shouldInline ? lookupDoc : group(indent([softline, lookupDoc])),
	]);
}

function shouldInlineMemberExpression(node: MemberExpression, objectDoc: Doc) {
	const { path } = getContext();
	const parent = getParentNode()!;
	let i = 0;
	let nmparent: Node | null = parent;
	while (nmparent && (is_MemberExpression(nmparent) || is_PostfixExpression(nmparent))) {
		nmparent = path.getParentNode(i++);
	}

	const shouldInline =
		(nmparent && (is_ExpressionPath(nmparent) || (is_ReassignmentNode(nmparent) && !is_Identifier(nmparent.left)))) ||
		!node.computed ||
		(is_Identifier(node.expression) && is_Identifier(node.property) && !is_MemberExpression(parent)) ||
		(is_AssignmentOrVariableDeclarator(parent) &&
			((is_CallExpression_or_CallLikeMacroInvocation(node.expression) && node.expression.arguments.length > 0) ||
				(is_PostfixExpression(node.expression) &&
					is_CallExpression_or_CallLikeMacroInvocation(node.expression.expression) &&
					node.expression.expression.arguments.length > 0) ||
				(objectDoc as any).label === "member-chain"));
	return shouldInline;
}

export function printAssignment(leftDoc: Doc, operator: string, rightPropertyName: string) {
	const assignmentNode = getNode();
	const rightNode = assignmentNode[rightPropertyName];

	if (!rightNode) return group(leftDoc);

	const layout = chooseLayout();
	const rightDoc = getPrintFn(assignmentNode)(rightPropertyName as any, { assignmentLayout: layout });
	const res = (function () {
		switch (layout) {
			case Layout["break-after-operator"]:
				return group([group(leftDoc), operator, group(indent([line, rightDoc]))]);
			case Layout["never-break-after-operator"]:
				return group([group(leftDoc), operator, " ", rightDoc]);
			case Layout["fluid"]: {
				const groupId = Symbol("assignment");
				return group([
					group(leftDoc),
					operator, //
					group(indent(line), { id: groupId }),
					lineSuffixBoundary,
					indentIfBreak(rightDoc, { groupId }),
				]);
			}
			case Layout["break-lhs"]:
				return group([leftDoc, operator, " ", group(rightDoc)]);
			case Layout["chain"]:
				return [group(leftDoc), operator, line, rightDoc];
			case Layout["chain-tail"]:
				return [group(leftDoc), operator, indent([line, rightDoc])];
			case Layout["chain-tail-arrow-chain"]:
				return [group(leftDoc), operator, rightDoc];
			default:
				exit.never();
		}
	})();

	return label(toLayout[layout], res);
	return res;

	function chooseLayout() {
		if (
			(is_ReassignmentExpression(assignmentNode) && is_printing_macro()) ||
			is_GenericParameterDeclaration(assignmentNode) ||
			is_TypeCallNamedArgument(assignmentNode)
		) {
			return Layout["never-break-after-operator"];
		}

		const isTail = !is_ReassignmentNode(rightNode);
		const shouldUseChainFormatting = getContext().path.match(
			is_ReassignmentNode,
			is_AssignmentOrVariableDeclarator,
			(node: Node) => !isTail || (!is_ExpressionStatement(node) && !is_VariableDeclarationNode(node))
		);

		if (shouldUseChainFormatting) {
			return !isTail
				? Layout["chain"]
				: is_ClosureFunctionExpression(rightNode) && is_ClosureFunctionExpression(rightNode.expression)
				? Layout["chain-tail-arrow-chain"]
				: Layout["chain-tail"];
		}

		const isHeadOfLongChain = !isTail && is_ReassignmentNode(rightNode.right);
		if (isHeadOfLongChain || hasLeadingOwnLineComment(rightNode)) {
			return Layout["break-after-operator"];
		}

		if (
			isComplexDestructuring(assignmentNode) ||
			hasComplexGenerics(assignmentNode) ||
			hasComplexTypeAnnotation(assignmentNode) ||
			(isArrowFunctionVariableDeclarator(assignmentNode) && canBreak(leftDoc))
		) {
			return Layout["break-lhs"];
		}

		const hasShortKey = isObjectPropertyWithShortKey(assignmentNode, leftDoc);

		if (pathCall(assignmentNode, rightPropertyName as never, (rightNode) => shouldBreakAfterOperator(rightNode, hasShortKey))) {
			return Layout["break-after-operator"];
		}

		if (hasShortKey || is_Literal(rightNode)) {
			return Layout["never-break-after-operator"];
		}

		return Layout["fluid"];
	}

	function shouldBreakAfterOperator(rightNode: Node, hasShortKey: boolean) {
		if (is_MemberExpression(rightNode) && shouldInlineMemberExpression(rightNode, getPrintFn(rightNode)("expression"))) {
			return false;
		}

		if (is_BinaryishExpression(rightNode) && !shouldInlineLogicalExpression(rightNode)) {
			return true;
		}

		if (is_IfBlockExpression(rightNode)) {
			return false;
		}

		if (hasShortKey) {
			return false;
		}

		return (function unwrap(node: Node) {
			if (is_UnaryExpression(node) || is_PostfixExpression(node)) {
				return pathCall(node, "expression", unwrap);
			}
			if (is_LiteralStringLike(node)) {
				return true;
			}
			return isPoorlyBreakableMemberOrCallChain(node);
		})(rightNode);

		function isPoorlyBreakableMemberOrCallChain(topNode: Node) {
			return (function unwrap(node: Node): boolean {
				if (is_MemberExpression(node) || is_PostfixExpression(node) || is_UnaryExpression(node)) {
					return pathCall(node, "expression", unwrap);
				}

				if (is_ExpressionPath(node)) {
					return pathCall(node, "namespace", (namespace) => !namespace || unwrap(namespace));
				}

				if (is_CallExpression_or_CallLikeMacroInvocation(node)) {
					const doc = printCallExpression(getPrintFn(), node);
					if ((doc as any).label === "member-chain") {
						return false;
					}

					const args = node.arguments;
					const isPoorlyBreakableCall = args.length === 0 || (args.length === 1 && isLoneShortArgument(args[0]));
					if (!isPoorlyBreakableCall) {
						return false;
					}

					if (hasComplexTypeArguments(node)) {
						return false;
					}

					return pathCall(node, "callee", unwrap);
				}

				return topNode === node ? false : is_Identifier(node);
			})(topNode);
		}
	}
}
function is_MemberExpression_with_RangeOrLiteral_Property(node: Node | undefined) {
	return (
		!!node && is_MemberExpression(node) && (node.computed ? is_Literal_or_SimpleRangeLiteral(node.property) : is_Literal(node.property))
	);
}
function is_Literal_or_SimpleRangeLiteral(node: Node) {
	return is_Literal(node)
		? true
		: is_RangeLiteral(node)
		? (!node.lower || is_Literal(node.lower)) && (!node.upper || is_Literal(node.upper))
		: false;
}
function printMemberLookup(print: print<MemberExpression>, node: MemberExpression) {
	return !node.computed
		? [".", print("property")]
		: is_Literal_or_SimpleRangeLiteral(node.property)
		? ["[", print("property"), "]"]
		: group(["[", indent([softline, print("property")]), softline, "]"]);
}

function shouldPrint_CallExpression_chain(node: CallExpression) {
	return is_MemberAccessLike(node.callee) || is_CallExpression_or_CallLikeMacroInvocation(node.callee);
}
export function is_MemberAccessLike(node: Node): node is ExpressionPath | MemberExpression {
	switch (node.nodeType) {
		case NodeType.ExpressionPath:
		case NodeType.MemberExpression:
			return true;
		default:
			return false;
	}
}
type ChainItem = { node: Node; printed: Doc; needsParens: boolean };
function printMemberChain(print: print<CallExpression>, node: CallExpression) {
	const parent = getParentNode();
	const isExpressionStatement = !parent || is_ExpressionStatement(parent);
	const { printedNodes, groups } = splitCallChains(node);

	const shouldMerge = groups.length >= 2 && !hasComment(groups[1][0].node) && shouldNotWrap(groups);

	const printedGroups = groups.map(printGroup);
	const oneLine = printedGroups;

	const cutoff = shouldMerge ? 3 : 2;

	const nodeHasComment =
		printedNodes.slice(1, -1).some(({ node }) => hasComment(node, CF.Leading)) ||
		printedNodes.slice(0, -1).some(({ node }) => hasComment(node, CF.Trailing)) ||
		(groups[cutoff] && hasComment(groups[cutoff][0].node, CF.Leading));

	if (groups.length <= cutoff && !nodeHasComment) {
		return isLongCurriedCallExpression(node) ? oneLine : group(oneLine);
	}

	const lastNodeBeforeIndent = last_of(groups[shouldMerge ? 1 : 0]).node;
	const shouldHaveEmptyLineBeforeIndent =
		!is_CallExpression_or_CallLikeMacroInvocation(lastNodeBeforeIndent) && shouldInsertEmptyLineAfter(lastNodeBeforeIndent);

	const expanded = [
		printGroup(groups[0]),
		shouldMerge ? groups.slice(1, 2).map(printGroup) : "",
		shouldHaveEmptyLineBeforeIndent ? hardline : "",
		printIndentedGroup(groups.slice(shouldMerge ? 2 : 1)),
	];

	const callExpressions = printedNodes.map(({ node }) => node).filter(is_CallExpression_or_CallLikeMacroInvocation);
	const result: Doc =
		nodeHasComment ||
		(callExpressions.length > 2 && callExpressions.some((expr) => expr.arguments.some((arg) => !isSimpleCallArgument(arg, 0)))) ||
		printedGroups.slice(0, -1).some(willBreak) ||
		lastGroupWillBreakAndOtherCallsHaveFunctionArguments()
			? group(expanded)
			: [shouldHaveEmptyLineBeforeIndent || willBreak(oneLine) ? breakParent : "", conditionalGroup([oneLine, expanded])];

	return label("member-chain", result);

	function shouldInsertEmptyLineAfter(node: Node) {
		let start = end(node);
		const last = getNextNonSpaceNonCommentCharacterIndex(node);
		const { originalText } = getContext().options;
		while (start < last) {
			if (originalText.charAt(start) === ")") {
				return isNextLineEmptyAfterIndex(start + 1);
			}
			start++;
		}
		return isNextLineEmpty(node);
	}

	function isFactory(name: string) {
		return /^[A-Z]|^[$_]+$/.test(name);
	}
	function isShort(name: string) {
		return name.length <= getContext().options.tabWidth;
	}

	function shouldNotWrap(groups: ChainItem[][]) {
		const hasComputed = groups[1].length > 0 && is_MemberExpression(groups[1][0].node) && groups[1][0].node.computed;

		if (groups[0].length === 1) {
			const firstNode = groups[0][0].node;
			return (
				is_Identifier(firstNode) && (isFactory(firstNode.name) || (isExpressionStatement && isShort(firstNode.name)) || hasComputed)
			);
		}

		const lastNode = last_of(groups[0]).node;
		const lastNodeLeft = is_ExpressionPath(lastNode)
			? lastNode.namespace
			: is_MemberExpression(lastNode)
			? lastNode.expression
			: undefined;
		return lastNodeLeft && is_Identifier(lastNodeLeft) && (isFactory(lastNodeLeft.name) || hasComputed);
	}

	function printGroup(g: ChainItem[]) {
		const printed: Doc[] = [];
		if (printedNodes[0] === g[0]) {
			for (const item of printedNodes) {
				if (item.needsParens) printed.unshift("(");
			}
		}
		for (const item of g) {
			printed.push(item.printed);
			if (item.needsParens) printed.push(")");
		}
		return printed;
	}

	function printIndentedGroup(groups: ChainItem[][]) {
		if (groups.length === 0) return "";
		return indent(group([hardline, join(hardline, groups.map(printGroup))]));
	}

	function lastGroupWillBreakAndOtherCallsHaveFunctionArguments() {
		const lastGroupNode = last_of(last_of(groups)).node;
		const lastGroupDoc = last_of(printedGroups);
		return (
			is_CallExpression_or_CallLikeMacroInvocation(lastGroupNode) &&
			willBreak(lastGroupDoc) &&
			callExpressions.slice(0, -1).some((node) => node.arguments.some(is_ClosureFunctionExpression))
		);
	}

	function splitCallChains(topNode: CallExpression | CallLikeMacroInvocation) {
		const printedNodes: ChainItem[] = [
			{
				node: topNode,
				needsParens: false,
				printed: print_CallExpression_end(print, node),
			},
		];

		pathCall(topNode, "callee", function READ_LEFT(node: Node) {
			if (is_CallExpression_or_CallLikeMacroInvocation(node) && shouldPrint_CallExpression_chain(node)) {
				__DEV__: Narrow<print<typeof node>>(print);

				unshift(print_CallExpression_end(print, node), shouldInsertEmptyLineAfter(node));
				pathCall(node, "callee", READ_LEFT);
			} else if (is_MemberExpression(node)) {
				__DEV__: Narrow<print<typeof node>>(print);

				unshift(printMemberLookup(print, node));
				pathCall(node, "expression", READ_LEFT);
			} else if (is_ExpressionPath(node)) {
				__DEV__: Narrow<print<typeof node>>(print);

				unshift(["::", print("segment")]);
				if (node.namespace) {
					pathCall(node, "namespace", READ_LEFT as any);
				}
			} else if (is_PostfixExpression(node)) {
				unshift(is_UnwrapExpression(node) ? "?" : ".await");
				pathCall(node, "expression", READ_LEFT);
			} else {
				printedNodes.unshift({ node, needsParens: false, printed: print() });
			}

			function unshift(printed: Doc, needsHardlineAfter = false) {
				printedNodes.unshift({
					node,
					needsParens: is_MemberAccessLike(node) && needsParens(node),
					printed: [withComments(node, printed), needsHardlineAfter ? hardline : ""],
				});
			}
		});

		const groups = spread(function* () {
			let i = 0;
			let currentItem = printedNodes[i];

			function testNextItem(fn: (item: ChainItem) => boolean) {
				return i + 1 < printedNodes.length && fn(printedNodes[i + 1]);
			}

			function readGroup(fn: () => Iterable<ChainItem>) {
				__DEV__: assert(i < printedNodes.length);
				return spread(function* () {
					for (var _item of fn()) {
						yield currentItem;
						if (++i < printedNodes.length) currentItem = printedNodes[i];
						else break;
					}
				});
			}

			function* loop(condition: (item: ChainItem) => boolean) {
				while (condition(currentItem)) yield currentItem;
			}
			function* until(condition: (item: ChainItem) => boolean) {
				while (!condition(currentItem)) yield currentItem;
			}

			yield readGroup(function* () {
				const isCallExpression = is_CallExpression_or_CallLikeMacroInvocation(currentItem.node);

				yield currentItem;
				yield* loop(
					({ node, needsParens }) =>
						is_PostfixExpression(node) ||
						is_CallExpression_or_CallLikeMacroInvocation(node) ||
						is_MemberExpression_with_RangeOrLiteral_Property(node) ||
						needsParens
				);

				if (!isCallExpression) {
					yield* loop(
						({ node, needsParens }) =>
							is_MemberAccessLike(node) && //
							testNextItem(({ node }) => is_MemberAccessLike(node))
					);
				}
			});

			while (i < printedNodes.length) {
				yield readGroup(function* () {
					let isCallExpression = false;

					yield* until(
						({ node }) =>
							(isCallExpression = is_CallExpression_or_CallLikeMacroInvocation(node)) || //
							hasComment(node, CF.Trailing)
					);
					yield currentItem;

					if (isCallExpression) {
						yield* loop(({ node }) => is_MemberExpression_with_RangeOrLiteral_Property(node));
						yield* until(
							({ node }) =>
								is_MemberAccessLike(node) || //
								hasComment(node, CF.Trailing)
						);
					}
				});
			}
		});
		return { printedNodes, groups };
	}
}

function isSimpleCallArgument(node: Node, depth: number) {
	if (depth >= 2) return false;

	if (is_IdentifierOrIndex(node)) {
		return true;
	}

	if (is_Literal(node)) {
		return !is_LiteralStringLike(node) || !node.value.includes("\n");
	}

	if (is_ArrayOrTupleLiteral(node)) {
		return node.items.every(isChildSimple);
	}

	if (is_StructLiteral(node)) {
		return (
			isSimpleCallArgument(node.struct, depth) &&
			node.properties.every((prop) =>
				is_StructLiteralPropertySpread(prop)
					? isChildSimple(prop.expression)
					: is_StructLiteralProperty(prop)
					? isChildSimple(prop.value)
					: true
			)
		);
	}

	if (is_CallExpression_or_CallLikeMacroInvocation(node)) {
		return (
			isSimpleCallArgument(node.callee, depth) &&
			(node.typeArguments ?? []).every(isChildSimple) &&
			node.arguments.every(isChildSimple)
		);
	}

	if (is_MemberExpression(node)) {
		return isSimpleCallArgument(node.expression, depth) && isSimpleCallArgument(node.property, depth);
	}

	if (is_ExpressionTypeCast(node)) {
		return isSimpleCallArgument(node.typeCallee, depth) && node.typeArguments.every(isChildSimple);
	}

	if (is_ExpressionPath(node)) {
		const namespace = node.namespace;
		return !namespace || isSimpleCallArgument(namespace, depth);
	}

	if (is_UnaryExpression(node) || is_PostfixExpression(node)) {
		return isSimpleCallArgument(node.expression, depth);
	}

	return false;

	function isChildSimple(child: Node) {
		return isSimpleCallArgument(child, depth + 1);
	}
}
function isLongCurriedCallExpression(node: Node) {
	const parent = getParentNode(node)!;
	return (
		is_CallExpression_or_CallLikeMacroInvocation(node) &&
		is_CallExpression_or_CallLikeMacroInvocation(parent) &&
		parent.callee === node &&
		node.arguments.length > parent.arguments.length &&
		parent.arguments.length > 0
	);
}

export function printTypeArguments<T extends Extract<Node, MaybeGenericArgsTarget>>(print: print<T>, node: T) {
	return !node.typeArguments
		? ""
		: node.typeArguments.length === 0
		? ["<", printDanglingCommentsForInline(node, DCM["typeArguments"]), ">"]
		: hasComplexTypeArguments(node)
		? group(
				[
					"<", //
					indent([softline, print.join("typeArguments", [",", line])]),
					softline,
					">",
				],
				{ id: getTypeParametersGroupId(node) }
		  )
		: ["<", print.join("typeArguments", ", "), ">"];
}

export function printLtParameters<T extends Extract<Node, ForLtParametersBody>>(print: print<T>, node: T) {
	return !node.ltParameters
		? ""
		: node.ltParameters.length === 0
		? ["for<", printDanglingCommentsForInline(node, DCM["ltParameters"]), "> "]
		: hasComplexLtParameters(node)
		? group(
				[
					"for<", //
					indent([softline, print.join("ltParameters", [",", line])]),
					softline,
					"> ",
				],
				{ id: getTypeParametersGroupId(node) }
		  )
		: ["for<", print.join("ltParameters", ", "), "> "];
}

export function printGenerics<T extends DeclarationNode>(print: print<T>, node: T) {
	return group(
		!node.generics
			? ""
			: hasComplexGenerics(node)
			? [
					"<",
					indent([softline, print.join("generics", [",", line])]), //
					hasMultipleHeritage(node) ? indent([softline, ">"]) : [softline, ">"],
			  ]
			: [
					"<",
					print.join("generics", ", "), //
					printDanglingCommentsForInline(node, DCM["generics"]),
					">",
			  ]
	);
}
function getPrintedTypeBounds<T extends Extract<Node, TypeBoundsConstaint>>(print: print<T>, node: T) {
	if (!hasTypeBounds(node) || node.typeBounds.length === 0) return "";
	if (node.typeBounds.length === 1) return print.map("typeBounds");
	// let shouldIndent = false;
	// const printed = print.map("typeBounds", (bound, i, arr) =>
	// 	0 === i
	// 		? print()
	// 		: true //isSimpleTypeBound(arr[i - 1]) && isSimpleTypeBound(bound)
	// 		? indent([" +", line, print()])
	// 		: [" + ", (shouldIndent ||= isSimpleTypeBound(arr[i - 1]) || isSimpleTypeBound(bound)) ? indent(print()) : print()]
	// );
	const printed = print.join("typeBounds", (_, __, prev) => (!prev ? " +" : [" +", line]));
	return [printed.shift()!, indent([line, printed])];
}
export function printTypeBounds<T extends Extract<Node, TypeBoundsConstaint>>(operator: "dyn" | "impl" | ":", print: print<T>, node: T) {
	if (!hasTypeBounds(node)) return "";
	const printed = getPrintedTypeBounds(print, node);
	return printed ? group([operator, " ", printed]) : operator;

	return !node.typeBounds
		? ""
		: hasComplexTypeBounds(node)
		? group(indent([ifBreak(line), operator, " ", join([line, "+ "], print.map("typeBounds"))]))
		: [operator, " ", join(" + ", print.map("typeBounds"))];
}

export function printLtBounds<T extends Extract<Node, MaybeHasLtBounds>>(left: Doc, print: print<T>, node: T) {
	return group(
		!node.ltBounds
			? ""
			: node.ltBounds.length === 0
			? [left, " "]
			: [left, " ", print.map("ltBounds", (typeBound, i) => (i === 0 ? print() : indent([line, "+ ", print()])))]
	);
}

function printWhereBounds<T extends DeclarationNode>(print: print<T>, node: T) {
	if (!node.whereBounds || node.whereBounds.length === 0) return "";
	return adjustDeclarationClause(
		node, //
		"where",
		print.join("whereBounds", [",", line])
	);
}
export function printDeclarationTypeBounds<T extends Extract<DeclarationNode, TypeBoundsConstaint>>(
	print: print<T>,
	node: T,
	operator: ":" | " ="
) {
	return hasTypeBounds(node) ? adjustDeclarationClause(node, operator, getPrintedTypeBounds(print, node)) : "";
}
export function printImplTraitForType(
	print: print<ImplDeclaration | NegativeImplDeclaration>,
	node: ImplDeclaration | NegativeImplDeclaration
) {
	return node.trait
		? [print("trait"), adjustDeclarationClause(node, "for", print("typeTarget"))] //
		: print("typeTarget");
}

function adjustDeclarationClause(node: DeclarationNode, clause: ":" | " =" | "for" | "->" | "where", content: Doc) {
	const isTypeBoundsClause = clause === ":" || clause === " =";
	return (clause === "where" || hasMultipleHeritage(node) ? indent : Identity)([
		clause === "->"
			? hasMultipleHeritage(node) && node.whereBounds!.length > 1
				? line
				: " "
			: clause === "where" //
			? line
			: isTypeBoundsClause
			? hasMultipleHeritage(node)
				? softline
				: ""
			: line,
		clause,
		content &&
			group(
				clause === "where"
					? indent([line, content]) //
					: clause === "->" || isTypeBoundsClause
					? [" ", content]
					: [line, content]
			),
	]);
}
function hasNonWhereHeritageClause(node: DeclarationNode) {
	AssertTypesEq<
		DeclarationNode,
		| FunctionDeclaration
		| StructDeclaration
		| TupleStructDeclaration
		| UnionDeclaration
		| TypeAliasDeclaration
		| TraitDeclaration
		| TraitAliasDeclaration
		| NegativeImplDeclaration
		| ImplDeclaration
		| EnumDeclaration
	>();
	switch (node.nodeType) {
		case NodeType.FunctionDeclaration:
			return !!node.returnType;
		case NodeType.StructDeclaration:
		case NodeType.TupleStructDeclaration:
		case NodeType.UnionDeclaration:
		case NodeType.EnumDeclaration:
			return false;
		case NodeType.TypeAliasDeclaration:
		case NodeType.TraitDeclaration:
		case NodeType.TraitAliasDeclaration:
			// case NodeType.AutoTraitDeclaration:
			return hasTypeBounds(node);
		case NodeType.ImplDeclaration:
		case NodeType.NegativeImplDeclaration:
			return !!node.trait;
		default:
			__DEV__: exit.never(node);
	}
}
function hasAnyHeritageClause(node: DeclarationNode) {
	return !!node.whereBounds || hasNonWhereHeritageClause(node);
}

function hasMultipleHeritage(node: DeclarationNode) {
	return !!node.whereBounds && hasNonWhereHeritageClause(node);
}

const getMacroGroupId = createGroupIdMapper("MacroGroup");
const getHeritageGroupId = createGroupIdMapper("heritageGroup");
const getWhereBoundsGroupId = createGroupIdMapper("where");
const getTypeParametersGroupId = createGroupIdMapper("typeParameters");
function createGroupIdMapper(description: string) {
	const groupIds = new WeakMap<Node, symbol>();
	return (node: Node) => Map_get(groupIds, node, () => Symbol(description));
}

export function printDanglingCommentsForInline(node: Node, marker?: DCM) {
	const hasOnlyBlockComments =
		!hasComment(node, CF.Line | CF.Dangling, (comment) => !marker || comment.marker === marker) || is_Program(node);
	const printed = printDanglingComments(node, hasOnlyBlockComments, marker);
	return (
		printed &&
		(hasOnlyBlockComments && !is_Program(node)
			? willBreak(printed)
				? [indent([hardline, printed]), hardline]
				: [printed]
			: [printed, hardline])
	);
}

function isFormatLikeCall(node: CallExpression | CallLikeMacroInvocation) {
	if (is_Identifier(node.callee) && !node.typeArguments) {
		const [first, ...rest] = node.arguments;
		if (is_Literal(first) && is_LiteralStringLike(first) && first.value.includes("{}") && rest.every(is_Identifier)) {
			return true;
		}
	}
	return false;
}

class ArgExpansionBailout extends Error {}

export function printCallArguments<T extends CallExpression | CallLikeMacroInvocation>(print: print<T>, node: T) {
	const args = node.arguments;
	const { left: LEFT, right: RIGHT } = getDelimChars(args);

	__DEV__: {
		// assert(args.length === 0 || !hasComment(node, CF.Dangling), "", node);
		if (is_MacroInvocation(node)) assert(args.every(is_ExpressionNode), "", node);
	}

	if (args.length === 0) return [LEFT, printDanglingCommentsForInline(node, DCM["arguments"]), RIGHT];

	// force inline format!(" {} ", ident)
	if (args.length === 2 && isFormatLikeCall(node)) {
		return [LEFT, print(["arguments", 0]), ", ", print(["arguments", 1]), RIGHT];
	}

	let anyArgEmptyLine = false;
	let hasEmptyLineFollowingFirstArg = false;
	const lastArgIndex = args.length - 1;
	const trailingComma = false ? "," : "";
	const printedArguments = print.map("arguments", (arg, index, arr) => {
		if (index === lastArgIndex) {
			return [print()];
		} else if (isNextLineEmpty(arg)) {
			if (index === 0) hasEmptyLineFollowingFirstArg = true;
			anyArgEmptyLine = true;
			return [print(), ",", hardline, hardline];
		} else {
			return [print(), ",", line];
		}
	});

	if (anyArgEmptyLine || isFunctionCompositionArgs(args)) {
		return allArgsBrokenOut();
	}

	const shouldGroupFirst = shouldGroupFirstArg(args);
	const shouldGroupLast = shouldGroupLastArg(args);
	if (shouldGroupFirst || shouldGroupLast) {
		if (shouldGroupFirst ? printedArguments.slice(1).some(willBreak) : printedArguments.slice(0, -1).some(willBreak)) {
			return allArgsBrokenOut();
		}
		let printedExpanded: Doc[] = [];
		const { path } = getContext();
		const stackBackup = [...path.stack];
		try {
			path_try(() => {
				getContext().path.each((p, i) => {
					if (shouldGroupFirst && i === 0) {
						printedExpanded = [
							[
								print([], { expandFirstArg: true }),
								printedArguments.length > 1 ? "," : "",
								hasEmptyLineFollowingFirstArg ? hardline : line,
								hasEmptyLineFollowingFirstArg ? hardline : "",
							],
							...printedArguments.slice(1),
						];
					}
					if (shouldGroupLast && i === lastArgIndex) {
						printedExpanded = [...printedArguments.slice(0, -1), print([], { expandLastArg: true })];
					}
				}, "arguments");
			});
		} catch (caught) {
			path.stack.length = 0;
			path.stack.push(...stackBackup);
			if (caught instanceof ArgExpansionBailout) return allArgsBrokenOut();
			throw caught;
		}

		return [
			printedArguments.some(willBreak) ? breakParent : "",
			conditionalGroup([
				[LEFT, ...printedExpanded, RIGHT],
				shouldGroupFirst
					? [LEFT, group(printedExpanded[0], { shouldBreak: true }), ...printedExpanded.slice(1), RIGHT]
					: [LEFT, ...printedArguments.slice(0, -1), group(printedExpanded[lastArgIndex], { shouldBreak: true }), RIGHT],
				allArgsBrokenOut(),
			]),
		];
	}

	const contents = [LEFT, indent([softline, ...printedArguments]), ifBreak(trailingComma), softline, RIGHT];

	return isLongCurriedCallExpression(node)
		? contents
		: group(contents, { shouldBreak: anyArgEmptyLine || printedArguments.some(willBreak) });

	function allArgsBrokenOut() {
		return group([LEFT, indent([line, ...printedArguments]), trailingComma, line, RIGHT], { shouldBreak: true });
	}
}

function shouldHugFunctionParameters(node: Extract<Node, FunctionLike>) {
	if (!node) return false;
	const parameters = getParameters(node);
	if (parameters.length !== 1) return false;
	const param = parameters[0];
	if (hasComment(param)) return false;
	switch (param.nodeType) {
		case NodeType.FunctionSelfParameterDeclaration:
		case NodeType.FunctionSpread:
		case NodeType.TypeFnPointerParameter:
		default:
			return false;
		case NodeType.FunctionParameterDeclaration:
		case NodeType.ClosureFunctionParameterDeclaration:
			return "items" in param.pattern || "properties" in param.pattern;
	}
}

function shouldGroupFunctionParameters(functionNode: FunctionDeclaration, returnTypeDoc: Doc) {
	const returnType = functionNode.returnType;
	const generics = functionNode.generics;
	const whereBounds = functionNode.whereBounds;
	if (!returnType) return false;
	if (generics) {
		if (generics.length > 1) return false;
		if (generics.length === 1 && !isShortGenericParameterDeclaration(generics[0])) return false;
	}
	if (whereBounds) {
		if (whereBounds.length > 1) return false;
	}
	return (
		getParameters(functionNode).length === 1 &&
		(willBreak(returnTypeDoc) || willBreak(printWhereBounds(getPrintFn(functionNode), functionNode)))
	);
}

export function printBlockBody<T extends NodeWithBodyOrCases | BlockLikeMacroInvocation>(print: print<T>, node: T): Doc {
	const body = printBodyOrCases(print, node);
	return [
		"{",
		body.length > 0
			? getBodyOrCases(node)?.length
				? canInlineBlockBody(node)
					? [indent([line, body]), line]
					: group([indent([line, body]), line], { shouldBreak: true })
				: body
			: emptyContent(node),
		"}",
	];
}

export function printMaybeBlockBody<T extends (NodeWithBodyOrCases | BlockLikeMacroInvocation) & { body: undefined | {} }>(
	print: print<T>,
	node: T
): Doc {
	return hasSemiNoBody(node) ? ";" : adjustClause(node, printBlockBody(print, node));
}

export function printArrowFunction<T extends ClosureFunctionExpression>(print: print<T>, node: T) {
	const signatures: Doc[] = [];
	const body: Doc[] = [];
	const { args, path } = getContext();
	let chainShouldBreak = false;

	let tailNode: ClosureFunctionExpression = node;
	(function rec(node: ClosureFunctionExpression) {
		tailNode = node;
		const doc = printArrowFunctionSignature(print, node);
		if (signatures.length === 0) {
			signatures.push(doc);
		} else {
			const { leading, trailing } = printCommentsSeparately();
			signatures.push([leading!, doc]);
			body.unshift(trailing!);
		}

		chainShouldBreak ||= !!node.returnType || !node.parameters.every((param) => isSimplePattern(param.pattern));

		if (!is_ClosureFunctionExpression(node.expression) || (args && args.expandLastArg)) {
			body.unshift(print("expression", args));
		} else {
			pathCall(node, "expression", rec);
		}
	})(node);

	if (signatures.length > 1) {
		return printArrowChain(signatures, chainShouldBreak, body, tailNode);
	} else {
		const printed = signatures[0];

		if (
			!hasLeadingOwnLineComment(node.expression) &&
			(is_ArrayOrTupleLiteral(node.expression) ||
				is_StructLiteral(node.expression) ||
				is_ExpressionWithBodyOrCases(node.expression) ||
				is_ClosureFunctionExpression(node.expression))
		) {
			return group([printed, " ", body]);
		}

		const shouldAddSoftLine = args && args.expandLastArg && !hasComment(node);
		const printTrailingComma = args && args.expandLastArg && false;
		const shouldAddParens = is_OrExpression(node.expression);

		return group([
			printed,
			group([
				indent(shouldAddParens ? [line, ifBreak("", "("), body, ifBreak("", ")")] : [line, body]),
				shouldAddSoftLine ? [ifBreak(printTrailingComma ? "," : ""), softline] : "",
			]),
		]);
	}
}

function printArrowChain(signatures: Doc[], shouldBreak: boolean, bodyDoc: Doc, tailNode: ClosureFunctionExpression) {
	const { args } = getContext();
	const parent = getParentNode()!;
	const isCallee = is_CallExpression_or_CallLikeMacroInvocation(parent) && parent.callee === getNode();
	const isAssignmentRhs = !!(args && args.assignmentLayout);
	const shouldPutBodyOnSeparateLine = !is_ExpressionWithBodyOrCases(tailNode.expression) && !is_StructLiteral(tailNode.expression);
	const shouldBreakBeforeChain =
		(isCallee && shouldPutBodyOnSeparateLine) || (args && args.assignmentLayout === Layout["chain-tail-arrow-chain"]);

	const groupId = Symbol("arrow-chain");

	return group([
		group(
			indent([isCallee || isAssignmentRhs ? softline : "", group(join(line, signatures), { shouldBreak })]), //
			{ id: groupId, shouldBreak: shouldBreakBeforeChain }
		),
		indentIfBreak(shouldPutBodyOnSeparateLine ? indent([line, bodyDoc]) : [" ", bodyDoc], { groupId }),
		isCallee ? ifBreak(softline, "", { groupId }) : "",
	]);
}

function printArrowFunctionSignature<T extends ClosureFunctionExpression>(print: print<T>, node: T) {
	const { args } = getContext();
	const expandArg = args && (args.expandLastArg || args.expandFirstArg);
	let returnTypeDoc: Doc = printReturnType(print, node);
	if (expandArg) {
		if (willBreak(returnTypeDoc)) throw new ArgExpansionBailout();
		else returnTypeDoc = group(removeLines(returnTypeDoc));
	}
	// const dangling = printDanglingComments(node, true, (comment) => node.parameters.loc.contains(comment));

	// if (dangling) {
	// 	parts.push(" ", dangling);
	// }
	return [
		print.b("static"),
		print.b("async"),
		print.b("move"), //
		group([printFunctionParameters(print, node, expandArg), returnTypeDoc]),
	];
}

export function printGenerics_x_whereBounds<T extends DeclarationNode>(print: print<T>, node: T, xDoc: Doc) {
	const generics: Doc = is_ImplDeclarationNode(node)
		? [printGenerics(print, node), " "]
		: [" ", print("id" as any), printGenerics(print, node)];

	const whereBoundsDoc = printWhereBounds(print, node);

	return is_TupleStructDeclaration(node)
		? [...generics, xDoc, group(whereBoundsDoc, { id: getHeritageGroupId(node) })]
		: [...generics, group([xDoc, whereBoundsDoc], { id: getHeritageGroupId(node) })];
}

export function adjustClause(
	node: DeclarationNode | ((NodeWithBodyOrCases | BlockLikeMacroInvocation) & { body: undefined | {} }),
	doc: Doc
) {
	return [
		"whereBounds" in node && (!!node.whereBounds || (hasTypeBounds(node) && node.typeBounds.length > 1)) && willBreak(doc)
			? ifBreak(hardline, " ", { groupId: getHeritageGroupId(node) })
			: " ",
		doc,
	];
}

export function printParametersAndReturnType(node: FunctionNode | TypeFunctionNode) {
	const parametersDoc = printFunctionParameters(getPrintFn(node), node);
	const returnTypeDoc = printReturnType(getPrintFn(node), node);
	return is_FunctionDeclaration(node) && shouldGroupFunctionParameters(node, returnTypeDoc)
		? group([group(parametersDoc), returnTypeDoc])
		: group([parametersDoc, returnTypeDoc]);
}

export function printFlowControlExpression<T extends ReturnExpression | BreakExpression | YieldExpression>(print: print<T>, node: T) {
	return !node.expression
		? ""
		: // : hasLeadingComment(node.expression)
		// ? [" (", indent([hardline, print("expression")]), hardline, ")"]
		is_BinaryishExpression(node.expression) && !flowControlExpressionNeedsOuterParens(node)
		? group([" ", ifBreak("("), indent([softline, print("expression")]), softline, ifBreak(")")])
		: [" ", print("expression")];

	function hasLeadingComment(node: Node) {
		if (hasLeadingOwnLineComment(node)) return true;
		if (hasNakedLeftSide(node)) {
			let leftMost = node;
			while ((leftMost = getLeftSide(leftMost))) {
				if (hasLeadingOwnLineComment(leftMost)) return true;
			}
		}
		return false;
	}
}
export function flowControlExpressionNeedsOuterParens(flow: ReturnExpression | BreakExpression | YieldExpression) {
	return (
		flow.expression &&
		(function hasLeadingComment(node: Node) {
			if (hasLeadingOwnLineComment(node)) return true;
			if (hasNakedLeftSide(node)) {
				let leftMost = node;
				while ((leftMost = getLeftSide(leftMost))) {
					if (hasLeadingOwnLineComment(leftMost)) return true;
				}
			}
			return false;
		})(flow.expression)
	);
}
function getLeftSide(node, includeAttributes = false) {
	let target: Node =
		(node as LeftRightExpression).left ??
		(node as CallExpression).callee ??
		(node as PathNode).namespace ??
		(node as ExpressionWithBody).label ??
		(node as RangeNode).lower ??
		(node as StructLiteral).struct ??
		(node as NodeWithCondition).condition ??
		(node as ExpressionBody).expression;
	if (target && includeAttributes && hasAttributes(node)) {
		(node.attributes as AttributeOrDocComment[]).forEach((attr) => {
			if (start(attr) < start(target)) target = attr;
		});
	}
	return target;
}
function hasNakedLeftSide(node: Node) {
	return (
		is_BinaryishExpression(node) ||
		is_ReassignmentNode(node) ||
		is_CallExpression_or_CallLikeMacroInvocation(node) ||
		is_MemberAccessLike(node) ||
		is_PostfixExpression(node) ||
		is_ExpressionAsTypeCast(node)
	);
}

export function printReturnType<T extends Extract<Node, MaybeReturnTypeConstraint>>(print: print<T>, node: T) {
	return node.returnType
		? is_FunctionDeclaration(node)
			? adjustDeclarationClause(node, "->", print("returnType"))
			: [" -> ", print("returnType")]
		: "";
}

function printFunctionParameters<T extends FunctionNode | TypeFunctionNode>(
	print: print<T>,
	node: T,
	expandArg = false,
	printTypeParams = false
) {
	const { left: leftDelim, right: rightDelim } = getDelimChars(node.parameters);
	const generics = printTypeParams && is_FunctionDeclaration(node) ? printGenerics(print as any, node) : "";

	if (!hasParameters(node)) {
		return [
			generics, //
			leftDelim,
			printDanglingCommentsForInline(node, DCM["parameters"]),
			rightDelim,
		];
	}

	const isParametersInTestCall = false;
	const shouldHugParameters = shouldHugFunctionParameters(node);
	const printed = print.join("parameters", sepFn);
	if (hasSelfParameter(node)) {
		printed.unshift(getContext().path.call(() => [print(), printed.length ? sepFn(node.parameters.self) : ""], "parameters", "self"));
	}

	if (expandArg) {
		if (willBreak(generics) || willBreak(printed)) throw new ArgExpansionBailout();
		return group([removeLines(generics), leftDelim, removeLines(printed), rightDelim]);
	} else if (shouldHugParameters || isParametersInTestCall) {
		return [generics, leftDelim, ...printed, rightDelim];
	} else {
		return [generics, leftDelim, indent([softline, ...printed]), softline, rightDelim];
	}
	function sepFn(param: Node) {
		return shouldHugParameters || isParametersInTestCall //
			? ", "
			: isNextLineEmpty(param)
			? [",", hardline, hardline]
			: [",", line];
	}
}

function path_try<T>(callback: () => T): T {
	const { stack } = getContext().path;
	const stackBackup = [...stack];
	try {
		return callback();
	} finally {
		stack.length = 0;
		stack.push(...stackBackup);
	}
}
function shouldGroupFirstArg(args: LocArray<ExpressionNode>): boolean {
	if (args.length !== 2) return false;
	const [firstArg, secondArg] = args;
	return (
		!hasComment(firstArg) &&
		is_ClosureFunctionExpression(firstArg) &&
		is_ExpressionWithBodyOrCases(firstArg.expression) &&
		!is_ClosureFunctionExpression(secondArg) &&
		!couldGroupArg(secondArg)
	);
}
function shouldGroupLastArg(args: LocArray<ExpressionNode>): boolean {
	const lastArg = last_of(args);
	const preLastArg = args[args.length - 2];
	return (
		!hasComment(lastArg, CF.Leading) &&
		!hasComment(lastArg, CF.Trailing) &&
		couldGroupArg(lastArg) &&
		(!preLastArg || preLastArg.nodeType !== lastArg.nodeType) &&
		(args.length !== 2 || !is_ClosureFunctionExpression(preLastArg) || !is_ArrayOrTupleLiteral(lastArg)) &&
		!(args.length > 1 && is_ArrayOrTupleLiteral(lastArg) && isConciselyPrintedArray(lastArg)) &&
		(args.length !== 1 || !is_IfBlockExpression(lastArg))
	);
}
function couldGroupArg(arg: Node, arrowChainRecursion = false): boolean {
	return (
		(is_StructLiteral(arg) && (arg.properties.length > 0 || hasComment(arg))) ||
		(is_ArrayOrTupleLiteral(arg) && (arg.items.length > 0 || hasComment(arg))) ||
		(is_ExpressionAsTypeCast(arg) && couldGroupArg(arg.expression)) ||
		(is_ClosureFunctionExpression(arg) &&
			(!arg.returnType || is_Identifier(arg.returnType) || !isNonEmptyBlockStatement(arg.expression)) &&
			(isNonEmptyBlockStatement(arg.expression) ||
				(is_ClosureFunctionExpression(arg.expression) && couldGroupArg(arg.expression, true)) ||
				is_StructLiteral(arg.expression) ||
				is_ArrayOrTupleLiteral(arg.expression) ||
				(!arrowChainRecursion && is_CallExpression_or_CallLikeMacroInvocation(arg.expression)))) ||
		is_ExpressionWithBodyOrCases(arg) //&& isNonEmptyBlockStatement(arg)
	);
}
function isNonEmptyBlockStatement(node: Node) {
	if (is_MatchExpression(node)) return node.cases.length > 0;
	return is_ExpressionWithBodyOrCases(node) && node.body.length > 0;
}
function isFunctionCompositionArgs(args) {
	if (args.length <= 1) {
		return false;
	}
	let count = 0;
	for (const arg of args) {
		if (is_ClosureFunctionExpression(arg)) {
			if (++count > 1) return true;
		} else if (is_CallExpression_or_CallLikeMacroInvocation(arg)) {
			for (const childArg of arg.arguments) {
				if (is_ClosureFunctionExpression(childArg)) {
					return true;
				}
			}
		}
	}
	return false;
}

export function printBinaryishExpression<T extends AndExpression | OrExpression | ComparisonExpression | OperationExpression>(
	print: print<T>,
	node: T
) {
	const parent = getParentNode()!;
	const grandParent = getGrandParentNode();
	const isInsideParenthesis = ("condition" in parent && parent.condition === node) || is_MatchExpression(parent);

	const parts = printBinaryishExpressions(false, isInsideParenthesis);
	if (isInsideParenthesis) return parts;
	if (
		(is_CallExpression_or_CallLikeMacroInvocation(parent) && parent.callee === node) || //
		is_UnaryExpression(parent) ||
		is_MemberExpression(parent)
	) {
		return group([indent([softline, ...parts]), softline]);
	}

	const shouldNotIndent =
		is_FlowControlExpression(parent) ||
		(is_ClosureFunctionExpression(parent) && parent.expression === node) ||
		is_ExpressionWithBodyOrCases(parent);

	const shouldIndentIfInlining =
		is_ReassignmentNode(parent) || is_VariableDeclarationNode(parent) || is_StructLiteral(parent) || is_StructLiteral(grandParent);

	const samePrecedenceSubExpression = is_BinaryishExpression(node.left) && shouldFlatten(node, node.left);

	if (
		shouldNotIndent ||
		(shouldInlineLogicalExpression(node) && !samePrecedenceSubExpression) ||
		(!shouldInlineLogicalExpression(node) && shouldIndentIfInlining)
	) {
		return group(parts);
	}

	if (parts.length === 0) return "";
	const firstGroupIndex = parts.findIndex((part) => typeof part !== "string" && !Array.isArray(part) && part.type === "group");
	const leading = parts.slice(0, firstGroupIndex === -1 ? 1 : firstGroupIndex + 1);
	return group([...leading, indent(parts.slice(leading.length))], { id: Symbol("logicalChain") });

	function printBinaryishExpressions(isNested: boolean, isInsideParenthesis: boolean): Doc[] {
		const { path, print, options } = getContext();
		const node = path.getValue();

		if (!is_BinaryishExpression(node)) {
			return [group(print())];
		}

		const parts: Doc[] = [];

		if (shouldFlatten(node, node.left)) {
			parts.push(...pathCall(node, "left", () => printBinaryishExpressions(true, isInsideParenthesis)));
		} else {
			parts.push(group(print("left")));
		}

		const shouldInline = shouldInlineLogicalExpression(node);
		const operator = node.kind;

		const right = [
			operator,
			shouldInline ? " " : line,
			// this is a hack (should always be 'print("right")')
			!shouldInline && is_LogicalExpression(node.right) && shouldFlatten(node.right, node)
				? pathCall(node, "right", () => printBinaryishExpressions(true, isInsideParenthesis))
				: print("right"),
		];

		const shouldBreak = hasComment(node.left, CF.Trailing | CF.Line);
		const shouldGroup =
			shouldBreak ||
			(!(isInsideParenthesis && is_LogicalExpression(node)) &&
				path.getParentNode()!.nodeType !== node.nodeType &&
				node.left.nodeType !== node.nodeType &&
				node.right.nodeType !== node.nodeType);

		parts.push(" ", shouldGroup ? group(right, { shouldBreak }) : right);

		if (isNested && hasComment(node)) {
			const printed = cleanDoc(withComments(node, parts));
			if (isConcat(printed) || (printed as any).type === "fill") {
				return getDocParts(printed) as Doc[];
			}

			return [printed];
		}

		return parts;
	}
}

// export function printLogicalExpression<T extends AndExpression | OrExpression>(print: print<T>, node: T) {
// 	if (!is_insideScrutinee(node)) return printBinaryishExpression(print, node);

// }

function shouldInlineLogicalExpression(node: Node) {
	if (is_LogicalExpression(node)) {
		if (is_StructLiteral(node.right)) return node.right.properties.length > 0;
		if (is_ArrayOrTupleLiteral(node.right)) return node.right.items.length > 0;
	}
	return false;
}

export function printUnaryExpression<T extends UnaryExpression>(leftDoc: Doc, node: T) {
	__DEV__: assert(is_UnaryExpression(node));
	// if (unaryNeedsOuterParens(node)) {
	// 	return [leftDoc, group(["(", indent([softline, getPrintFn<UnaryExpression>()("expression")]), softline, ")"])];
	// } else {
	const printed = getPrintFn(node)("expression");

	return group([leftDoc, printed]);
	// }
}

export function printIfBlock<T extends IfBlockExpression>(print: print<T>, node: T) {
	let printed: Doc = [
		printIfBlockCondition(print, node), //
		printBlockBody(print, node),
		f` else ${print("else")}`,
	];

	const parent = getParentNode()!;

	if (is_ClosureBlock(node, parent)) {
		printed = parenthesize_if_break([indent([softline, printed]), softline]);
	} else if (!is_ElseBlock(node, parent)) {
		// if (needsParens(node)) {
		// 	printed = group(printed);
		// }
		// if (needsParens(node)) {
		// 	printed = group([indent([softline, printed]), softline]);
		// } else {
		// printed = group(printed);
		// }
	}

	return printed;
}

export function printIfBlockCondition<T extends IfBlockExpression | MatchExpressionCase>(print: print<T>, node: T) {
	if (!hasCondition(node)) return "";
	// const id = Symbol("if");
	// return ["if ", group(printCondition(print, node), { id }), ifBreak("", " ", { groupId: id })];
	return f`if ${printCondition(print, node)}`;
}

// export function printMatchExpressionExpression<T extends MatchExpression>(print: print<T>, node: T) {
// 	return ["match ", maybe_parenthesize_condition(print, node), " "];
// }

export function printCondition<T extends IfBlockExpression | MatchExpressionCase | WhileBlockExpression>(print: print<T>, node: T) {
	return pathCall(node, "condition", (condition) => {
		if (!condition) return "";
		if (needsParens(condition)) return [print(), " "];
		const id = Symbol("condition");
		const softlineStart = true; //!is_LetScrutinee(condition); //!is_LetScrutinee(getLeftMostCondition(condition));
		const printed = [indent([softlineStart ? softline : "", print()]), softline];
		return [group(printed, { id }), ifBreak("", " ", { groupId: id })];
		return hasLetScrutineeCondition(node)
			? printed // parenthesizing nested "let" throws 'unused_parens'
			: parenthesize_if_break(printed);
	});
}
export function parenthesize_if_break(doc: Doc) {
	return conditionalGroup([doc, ["(", doc, ")"]], { shouldBreak: willBreak(doc) });
}
function unwrapParenthesized(doc: Doc) {
	__DEV__: {
		const check = (doc: Doc) => Array.isArray(doc) && doc.length === 3 && doc[0] === "(" && doc[2] === ")";
		assert(Array.isArray(doc) && check(doc.length === 1 ? doc[0] : doc), "Expected ['(', Doc, ')']", doc);
	}
	return doc.length === 1 ? doc[0][1] : doc[1];
}
export function withParensIdentOnBreak(node: Node, printed: Doc) {
	return printed;
	const parens = needsParens(node);
	const grouped = group([indent([softline, parens ? unwrapParenthesized(printed) : printed]), softline]);
	return conditionalGroup([printed, parens ? ["(", grouped, ")"] : grouped], { shouldBreak: willBreak(printed) });
}
function isSimplePattern(node: PatternNode | undefined) {
	if (!node) return false;
	switch (node.nodeType) {
		case NodeType.MacroInvocation:
			return false;
		case NodeType.ExpressionTypeCast:
			return isSimplePattern(node.typeCallee) && !hasComplexTypeArguments(node);
		case NodeType.ExpressionTypeSelector:
			return is_Identifier(node.typeTarget) && (!node.typeExpression || is_Identifier(node.typeExpression));
		case NodeType.ExpressionPath:
			return !node.namespace || isSimplePattern(node.namespace);
		case NodeType.RangePattern:
			return (!node.lower || isSimplePattern(node.lower)) && (!node.upper || isSimplePattern(node.upper));
		case NodeType.PatternVariableDeclaration:
		case NodeType.ReferencePattern:
		case NodeType.BoxPattern:
		case NodeType.MinusPattern:
			return isSimplePattern(node.pattern);
		case NodeType.Identifier:
		case NodeType.Literal:
		case NodeType.RestPattern:
		case NodeType.WildcardPattern:
			return true;
		default:
			return false;
	}
}

export function printUnionPattern<T extends UnionPattern>(print: print<T>, node: T) {
	if (node.patterns.length === 1) return print.map("patterns");
	const parent = getParentNode();
	const prebreak = parent && (is_VariableDeclarationNode(parent) || is_LetScrutinee(parent)) && !needsParens(node);
	return group([
		prebreak ? softline : "",
		print.map("patterns", (node, i, arr) => [
			withComments(node, [
				i === 0 //
					? ifBreak("| ")
					: "| ",
				align(2, print()),
			]),
			i === arr.length - 1 ? "" : line,
		]),
	]);
}

export function printArrayLike<T extends ArrayLikeNode>(print: print<T>, node: T) {
	const delims = getDelimChars(node.items);
	if (node.items.length === 0) {
		const comments = printDanglingCommentsForInline(node, DCM["items"]);
		return comments ? group([delims.left, comments, delims.right]) : delims.left + delims.right;
	}

	const groupId = Symbol("array");
	const shouldBreak =
		// is_TupleStructDeclaration(node) ||
		!is_TupleNode(node) &&
		node.items.length > 1 &&
		(node.items as (ExpressionNode | PatternNode)[]).every((item, i) => {
			const next = node.items[i + 1];
			return (
				((hasProperties(item) && item.properties.length > 1) || (hasItems(item) && item.items.length > 1)) &&
				(!next || item.nodeType === next.nodeType)
			);
		});
	const shouldUseConciseFormatting = isConciselyPrintedArray(node);

	const parent = getParentNode(node)!;
	const needsForcedTrailingComma =
		node.items.length === 1
			? is_TupleLiteral(node)
				? is_RangeLiteral(node.items[0])
					? !(is_ReassignmentExpression(parent) && parent.left === node)
					: true
				: is_TuplePattern(node)
				? !node.struct && !is_RangePattern(node.items[0]) && !is_RestPattern(node.items[0])
				: is_TypeTuple(node)
				? true
				: false
			: false;
	const trailingComma: Doc = needsForcedTrailingComma
		? "," //
		: shouldUseConciseFormatting
		? ifBreak(",", "", { groupId })
		: ifBreak(",");

	const printed = shouldUseConciseFormatting //
		? fill(
				print.join(
					"items",
					(item, next) =>
						isNextLineEmpty(item)
							? [",", hardline, hardline]
							: hasComment(next, CF.Leading, (comment) => is_LineCommentNode(comment) || comment.placement === "ownLine")
							? [",", hardline]
							: [",", line],
					trailingComma
				)
		  )
		: print.map_join(
				"items",
				() => group(print()),
				(item) => (isNextLineEmpty(item) ? [",", line, softline] : [",", line]),
				trailingComma
		  );

	return group([delims.left, indent([softline, printed]), printDanglingComments(node, true, DCM["items"]), softline, delims.right], {
		shouldBreak,
		id: groupId,
	});
}

export function printObject<T extends ObjectNode>(print: print<T>, node: T): Doc {
	if (hasSemiNoProperties(node)) {
		return ";";
	}
	if (!hasProperties(node)) {
		return [" {", printDanglingCommentsForInline(node, DCM["properties"]) || emptyContent(node), "}"];
	}

	const firstProperty = node.properties[0];

	const parent = getParentNode()!;

	const shouldBreak = is_StructPattern(node)
		? false
		: is_UnionDeclaration(node) ||
		  is_StructDeclaration(node) ||
		  is_EnumMemberStructDeclaration(node) ||
		  hasNewlineInRange(start(node), start(firstProperty));

	const content = [
		" {",
		indent([
			line,
			...print.join(
				"properties", //
				(node) => (isNextLineEmpty(node) ? [",", hardline, hardline] : [",", line]),
				(node) => (is_StructSpread(node) ? "" : ifBreak(","))
			),
		]),
		line,
		"}",
	];

	const grandparent = getGrandParentNode();
	if (
		grandparent &&
		(is_FunctionDeclaration(grandparent) || is_ClosureFunctionExpression(grandparent)) &&
		getParameters(grandparent)[0] === parent
	) {
		return content;
	}

	if (
		(is_StructLiteral(node) && is_ReassignmentNode(parent) && parent.left === node) ||
		(is_StructPattern(node) &&
			(is_VariableDeclarationNode(parent) || is_MatchExpressionCase(parent) || is_FunctionParameterDeclaration(parent)) &&
			parent.pattern === node)
	) {
		return content;
	}

	return group(content, { shouldBreak });
}

export function printEnumBody<T extends EnumDeclaration>(print: print<T>, node: T): Doc {
	const printed = print.join("members", (member) => [",", maybeEmptyLine(member)], ",");
	return [
		" {",
		printed.length === 0
			? printDanglingCommentsForInline(node, DCM["members"]) || emptyContent(node)
			: [indent([hardline, ...printed]), hardline],
		"}",
	];
}


================================================
FILE: src/format/external.ts
================================================
import { Attribute, AttributeOrDocComment, Comment, DocCommentAttribute, LocArray, MemberExpression, Node, SourceFile } from "jinx-rust";
import { PickProps } from "jinx-rust/utils";
import type { Doc, ParserOptions, Printer } from "prettier";
import doc from "prettier/doc.js";
import { AssertTypesEq } from "../utils/common";

export type { Doc, ParserOptions, Plugin, Printer } from "prettier";

declare module "prettier/doc.js" {
	namespace utils {
		function canBreak(doc: Doc): boolean;
	}
}

export const {
	join,
	line,
	softline,
	hardline,
	literalline,
	group,
	conditionalGroup,
	fill,
	lineSuffix,
	lineSuffixBoundary,
	cursor,
	breakParent,
	ifBreak,
	trim,
	indent,
	indentIfBreak,
	align,
	addAlignmentToDoc,
	markAsRoot,
	dedentToRoot,
	dedent,
	hardlineWithoutBreakParent,
	literallineWithoutBreakParent,
	label,
} = doc.builders;

export const {
	isConcat,
	getDocParts,
	willBreak,
	traverseDoc,
	findInDoc,
	mapDoc,
	propagateBreaks,
	removeLines,
	stripTrailingHardline,
	normalizeParts,
	normalizeDoc,
	cleanDoc,
	canBreak,
} = doc.utils;

export const Symbol_comments = Symbol.for("comments");

export interface CustomOptions extends ParserOptions<Node> {
	[Symbol_comments]: AnyComment[];
	rsParsedFile: SourceFile;
	commentSpans: Map<number, number>;
	printer: Printer<Node>;
	cursorNode: any;

	comments: Comment[];
	danglingAttributes: AttributeOrDocComment[];
	actuallyMethodNodes: WeakSet<MemberExpression>;
}

export type NodeWithComments<T extends Node> = T & { comments: AnyComment[] };
export interface MutatedComment extends Comment, PrettierCommentInfo {}
export interface MutatedAttribute extends Attribute, PrettierCommentInfo {}
export interface MutatedDocComment extends DocCommentAttribute, PrettierCommentInfo {}
export type AnyComment = MutatedComment | MutatedAttribute | MutatedDocComment;

type keyofDelimitedArrayProps<T> = T extends never ? never : keyof PickProps<T, LocArray<any, "()" | "[]" | "{}" | "<>">>;

__DEV__: AssertTypesEq<keyof typeof DCM, keyofDelimitedArrayProps<Node>>();

export enum DCM {
	"arguments" = "arguments",
	"parameters" = "parameters",
	"items" = "items",
	"properties" = "properties",
	"members" = "members",
	"body" = "body",
	"cases" = "cases",
	"typeArguments" = "typeArguments",
	"ltParameters" = "ltParameters",
	"generics" = "generics",
	"specifiers" = "specifiers",
	"rules" = "rules",
	"match" = "match",
	"transform" = "transform",
	"segments" = "segments",
}

export interface PrettierCommentInfo {
	trailing: boolean;
	leading: boolean;
	unignore: boolean;
	printed: boolean;
	placement: "ownLine" | "endOfLine" | "remaining";
	// nodeDescription?: any;
	marker?: DCM;
}

export interface AstPath<T = Node> {
	stack: (Node | string | number)[];
	callParent<R>(callback: (path: this) => R, count?: number): R;
	getName(): PropertyKey | null;
	getValue(): T;
	getNode(count?: number): T | null;
	getParentNode(count?: number): T | null;

	match(...predicates: ((node: Node, name: string | null, number: number | null) => boolean)[]): boolean;

	call<R>(callback: (path: AstPath, index: number, value: any) => R, ...props: (string | number)[]): R;
	each(callback: (path: AstPath, index: number, value: any) => void, ...props: (string | number)[]): void;
	map<R>(callback: (path: AstPath, index: number, value: any) => R, ...props: (string | number)[]): R[];
}


================================================
FILE: src/format/plugin.ts
================================================
import { AttributeOrComment, IfBlockExpression, Node, Program, rs } from "jinx-rust";
import {
	ArrayProps,
	BoolProps,
	NodeProps,
	end,
	hasAttributes,
	insertNodes,
	is_Attribute,
	is_BlockCommentKind,
	is_BlockCommentNode,
	is_Comment,
	is_DocCommentAttribute,
	is_ElseBlock,
	is_LineCommentNode,
	is_MacroInvocation,
	is_MacroRule,
	is_MissingNode,
	is_Node,
	is_PunctuationToken,
	is_UnionPattern,
	start,
} from "jinx-rust/utils";
import { getCommentChildNodes, isTransformed, transform_ast } from "../transform";
import { Narrow, assert, color, each, exit, iLast, is_array, map_tagged_template, print_string } from "../utils/common";
import {
	CF,
	escapeComments,
	getComments,
	handleEndOfLineComment,
	handleOwnLineComment,
	handleRemainingComment,
	hasBreaklineAfter,
	hasComment,
	isDangling,
	isPrettierIgnoreAttribute,
	setDidPrintComment,
	withComments,
} from "./comments";
import { withCheckContext } from "./complexity";
import { isNoopExpressionStatement, maybeEmptyLine } from "./core";
import { AstPath, CustomOptions, Doc, Plugin, Symbol_comments, group, hardline, indent, line, softline } from "./external";
import { printer } from "./printer";
import { needsInnerParens, needsOuterSoftbreakParens, shouldPrintOuterAttributesAbove } from "./styling";

export function is_printing_macro() {
	return getContext().path.stack.some((node) => is_Node(node) && (is_MacroInvocation(node) || is_Attribute(node)));
}

export function assertPathAtNode(name: string, node: Node, ...ctx: any[]) {
	__DEV__: if (getNode() !== node)
		exit(`Attempted to call ${name}() in wrong prettier path context`, { asserted: node, actual: getNode() }, ...ctx);
}

export function f(...args: [strings: TemplateStringsArray, ...values: Doc[]]) {
	let cancel = false;
	const res = map_tagged_template(args, (doc) => {
		cancel ||= !doc || (is_array(doc) && doc.length === 0);
		return doc;
	});
	return cancel ? "" : res;
}

export function sg_single(s: TemplateStringsArray, v_0: Doc) {
	return group([s[0], indent([softline, v_0]), softline, s[1]]);
}
export function sg_duo(s: TemplateStringsArray, v_0: Doc, v_1: Doc) {
	return group([s[0], indent([softline, v_0, s[1], line, v_1]), softline, s[2]]);
}

let ctx: {
	path: AstPath;
	options: CustomOptions;
	print: (path?: AstPath | string | [], args?: any) => Doc;
	args: any;
};

export const getNode = () => ctx.path.stack[ctx.path.stack.length - 1] as Node;
export const stackIncludes = (x: Node | string | number) => ctx.path.stack.includes(x);
export const getContext = () => ctx;
export const getOptions = () => ctx.options;
export const getProgram = () => ctx.options.rsParsedFile.program;
export const getAllComments = () => ctx.options[Symbol_comments];
export const getParentNode = (child?: Node) => {
	__DEV__: if (child) assertPathAtNode("getParentNode", child);
	return ctx.path.getParentNode();
};
export const getGrandParentNode = () => ctx.path.getParentNode(1) as Node;
export const getPrintFn = <T extends Node>(forNode?: T | undefined): print<T> => {
	__DEV__: if (forNode) assertPathAtNode("getPrintFn", forNode);
	return print as print<T>;
};

const get = (property: keyof any) => getNode()[property];
const has = (property: keyof any) => !!get(property);

export function pathCall<T extends Node, K extends keyof NodeProps<T> & keyof T, R>(node: T, key: K, fn: (child: T[K]) => R): R {
	return ctx.path.call(() => fn(getNode() as any), key as any);
}

export function pathCallEach<T extends Node, K extends AK<T>>(
	node: T,
	key: K, // @ts-expect-error
	fn: (child: NonNullable<T[K]>[number], index: number) => void
) {
	__DEV__: assertPathAtNode("", node); // @ts-expect-error
	ctx.path.each((_, i) => fn(getNode() as any, i), key);
}

export function pathCallAtParent<T extends Node, R>(parent: T, fn: (parent: T) => R): R {
	return ctx.path.callParent(() => {
		__DEV__: assertPathAtNode("pathCallParent", parent);
		return fn(parent);
	});
}
export function pathCallParentOf<T extends Node, R>(child: Node, fn: (parent: T) => R): R {
	__DEV__: assertPathAtNode("pathCallParentOf", child);
	return ctx.path.callParent((p) => fn(getNode() as any));
}

export function pathCallTopMostIfBlockExpression<R>(node: IfBlockExpression, fn: (node: IfBlockExpression) => R): R {
	const parent = getParentNode(node)!;
	return is_ElseBlock(node, parent) ? pathCallAtParent(parent, (parent) => pathCallTopMostIfBlockExpression(parent, fn)) : fn(node);
}

function print(property?: any, args?: any): Doc | Doc[] {
	if (!property) return ctx.print(undefined!, args);
	if (Array.isArray(property)) return ctx.print(property as any, args);
	const value = get(property);
	return !!value ? (Array.isArray(value) ? ctx.path.map(ctx.print, property) : ctx.print(property, args)) : "";
}

namespace print {
	export function b(property: string, res = `${property} `): Doc {
		return has(property) ? res : "";
	}
	export function map(property: string, mapItem?: MapFn<any, any>): Doc[] {
		return !has(property) ? [] : ctx.path.map(mapItem ? (p, i, a) => mapItem(a[i], i, a) : () => ctx.print(), property);
	}
	export function join(property: string, sep: SepFn<any, any> | Doc, trailingSep: TrailingSepFn<any, any> | Doc = ""): Doc[] {
		return map_join(property, () => ctx.print(), sep, trailingSep);
	}
	export function map_join(
		property: string,
		mapFn: MapFn<any, any>,
		sep: SepFn<any, any> | Doc,
		sepTrailing: TrailingSepFn<any, any> | Doc = ""
	) {
		const sepFn = typeof sep === "function" ? sep : () => sep;
		return map(property, (v, i, a) => [
			mapFn(v, i, a),
			iLast(i, a as any)
				? typeof sepTrailing === "function"
					? sepTrailing(v)
					: sepTrailing
				: sepFn(v, a[i + 1], i === 0 ? undefined : a[i - 1]),
		]);
	}
}

// prettier-ignore
type SepFn<T extends Node = Node, K extends AK<T> = AK<T>> = <A extends AV<T, K>>(item: A[number], next_item: A[number], prev_item: A[number] | undefined) => Doc;
type MapFn<T extends Node = Node, K extends AK<T> = AK<T>> = <A extends AV<T, K>>(item: A[number], index: number, arr: A) => Doc;
type TrailingSepFn<T extends Node = Node, K extends AK<T> = AK<T>> = <A extends AV<T, K>>(item: A[number]) => Doc;
type AV<T extends Node, K extends keyof T> = Extract<NonNullable<T[K]>, ReadonlyArray<unknown>>;
type AK<T extends Node> = keyof ArrayProps<T> & keyof T;
// type AK<T extends Node> = keyof PickProps<T, {nodeType:number}|{nodeType:number}[]> & keyof T;

export interface print<T extends Node> {
	(property?: [], args?: any): Doc;
	(property?: [AK<T>, number], args?: any): Doc;
	(property?: AK<T>, args?: any): Doc[];
	// (property?: T extends {rules:{nodeType:number}|{nodeType:number}[]} ? "rules" : never, args?: any): Doc[];
	(property?: keyof NodeProps<T> & keyof T, args?: any): Doc;
	b(property: keyof BoolProps<T>, res?: string): Doc;
	map<K extends AK<T>>(property: K & keyof ArrayProps<T>, mapFn?: MapFn<T, K>): Doc[];
	join<K extends AK<T>>(property: K, sep: SepFn<T, K> | Doc, trailingSep?: TrailingSepFn<T, K> | Doc): Doc[];
	map_join<K extends AK<T>>(property: K, mapFn: MapFn<T, K>, sep: SepFn<T, K> | Doc, trailingSep?: TrailingSepFn<T, K> | Doc): Doc[];
}

function genericPrint() {
	return withCheckContext(() => {
		const node = getNode();
		__DEV__: assert(node.nodeType in printer);

		let printed: Doc = hasPrettierIgnore(node) //
			? node.loc.getOwnText()
			: printer[node.nodeType]!(print as any, node as never);

		const inner_parens = needsInnerParens(node);

		if (inner_parens) {
			printed = group(["(", printed, ")"]);
		}

		if (hasAttributes(node)) {
			const print_above = shouldPrintOuterAttributesAbove(node); /*  || node.attributes.length > 1 */
			printed = [
				...print.join(
					"attributes",
					(attr) =>
						print_above
							? maybeEmptyLine(attr)
							: is_LineCommentNode(attr) || (is_BlockCommentNode(attr) && hasBreaklineAfter(attr))
							? hardline
							: " ",
					(attr) =>
						print_above && is_DocCommentAttribute(attr)
							? maybeEmptyLine(attr)
							: print_above || is_LineCommentNode(attr) || (is_BlockCommentNode(attr) && hasBreaklineAfter(attr))
							? hardline
							: " "
				),
				printed,
			];
		}

		printed = withComments(
			node,
			printed,
			hasPrettierIgnore(node) || ((is_Attribute(node) || is_MacroInvocation(node)) && !isTransformed(node))
				? escapeComments(0, (comment) => node.loc.ownContains(comment))
				: is_MacroRule(node)
				? escapeComments(0, (comment) => node.transform.loc.contains(comment))
				: is_UnionPattern(getParentNode() ?? ({ nodeType: 0 } as any))
				? new Set(getComments(node, 0, (comment) => !isDangling(comment)))
				: undefined
		);

		if (!inner_parens && needsOuterSoftbreakParens(node)) {
			printed = [group(["(", indent([softline, printed]), softline, ")"])];
		}

		return printed;
	});

	function hasPrettierIgnore(node: Node) {
		return (
			(node as any).prettierIgnore ||
			hasComment(node, CF.PrettierIgnore) ||
			(hasAttributes(node) && node.attributes.some(isPrettierIgnoreAttribute))
		);
	}
}

export function canAttachComment(n: Node) {
	return !is_Comment(n) && !isNoopExpressionStatement(n) && !is_MissingNode(n) && !is_PunctuationToken(n);
}

export const plugin: Plugin<Node> = {
	languages: [
		{
			name: "Rust",
			aliases: ["rs"],
			parsers: ["jinx-rust"],
			extensions: [".rs", ".rs.in"],
			linguistLanguageId: 327,
			vscodeLanguageIds: ["rust"],
			tmScope: "source.rust",
			aceMode: "rust",
			codemirrorMode: "rust",
			codemirrorMimeType: "text/x-rustsrc",
		},
	],
	parsers: {
		"jinx-rust": {
			astFormat: "jinx-rust",
			locStart: start,
			locEnd: end,
			parse(code, parsers, options: CustomOptions) {
				ctx = { options } as any;

				options.rsParsedFile = rs.parseFile((options.originalText = code), { filepath: options.filepath });

				options.actuallyMethodNodes = new WeakSet();
				options.danglingAttributes = [];
				options.comments = [];

				transform_ast(options);

				const comments: AttributeOrComment[] = [];
				insertNodes(comments, options.comments);
				insertNodes(comments, options.danglingAttributes);

				// @ts-expect-error
				options.rsParsedFile.program.comments = comments;

				options.commentSpans = new Map(comments.map((n) => [start(n), end(n)]));

				return options.rsParsedFile.program;
			},
		},
	},
	printers: {
		"jinx-rust": {
			preprocess: (node: Program) => node.loc.src,
			print(path, options, print, args) {
				if (path.stack.length === 1) {
					__DEV__: Narrow<CustomOptions>(options);
					ctx = { path, options, print, args };
					try {
						const printed = genericPrint();
						__DEV__: devEndCheck(printed);
						return printed;
					} finally {
						ctx = undefined!;
					}
				} else if (args || ctx.args) {
					const prev_args = ctx.args;
					try {
						ctx.args = args;
						return genericPrint();
					} finally {
						ctx.args = prev_args;
					}
				} else {
					return genericPrint();
				}
			},
			hasPrettierIgnore: () => false,
			willPrintOwnComments: () => true,
			isBlockComment: is_BlockCommentKind,
			canAttachComment: canAttachComment,
			getCommentChildNodes: getCommentChildNodes,
			printComment: genericPrint,
			handleComments: {
				// @ts-expect-error
				avoidAstMutation: true,
				ownLine: handleOwnLineComment,
				endOfLine: handleEndOfLineComment,
				remaining: handleRemainingComment,
			},
		},
	},
	options: {},
	defaultOptions: {
		// default prettier (2)  -> rustfmt (4)
		tabWidth: 4,
		// default prettier (80) -> rustfmt (100)
		printWidth: 100,
	},
};

function devEndCheck(printed: Doc) {
	let first = false;
	const comments = getAllComments();
	each(comments, (comment, index) => {
		if (!comment.printed) {
			if (!first) (first = true), console.log(color.red(`Unprinted comments:`));
			const len = 40;
			const msg =
				color.magenta(
					(comment.marker ? `Dangling "${comment.marker}" ` : "") +
						(is_Attribute(comment) ? "Attribute " : is_DocCommentAttribute(comment) ? "DocCommentAttribute" : "Comment") +
						` ${index}/${comments.length}` +
						color.yellow(` ${print_string(comment.loc.sliceText(0, len))}${comment.loc.len() > len ? " ..." : ""}`)
				) + color.grey(`\n    at ${comment.loc.url()}`);
			if (globalThis.TESTS_FORMAT_DEV) exit(msg);
			else console.log(msg);
			setDidPrintComment(comment);
		}
	});
}


================================================
FILE: src/format/printer.ts
================================================
import { DelimKind, Node, NodeType, NTMap } from "jinx-rust";
import {
	getDelimChars,
	hasSuffix,
	is_ArrayOrTupleLiteral,
	is_BlockExpression,
	is_ClosureFunctionExpression,
	is_Identifier,
	is_IfBlockExpression,
	is_LiteralNumberLike,
	is_StructLiteral,
	start,
} from "jinx-rust/utils";
import {
	BlockLikeMacroInvocation,
	CallLikeMacroInvocation,
	is_BlockLikeMacroInvocation,
	is_CallLikeMacroInvocation,
	isTransformed,
} from "../transform";
import { exit } from "../utils/common";
import { hasComment, print_comment } from "./comments";
import { isSimpleType } from "./complexity";
import {
	adjustClause,
	parenthesize_if_break,
	printAnnotatedPattern,
	printArrayLike,
	printArrowFunction,
	printAssignment,
	printBinaryishExpression,
	printBlockBody,
	printBodyOrCases,
	printCallArguments,
	printCallExpression,
	printCondition,
	printDanglingCommentsForInline,
	printDeclarationTypeBounds,
	printEnumBody,
	printFlowControlExpression,
	printGenerics_x_whereBounds,
	printIfBlock,
	printIfBlockCondition,
	printImplTraitForType,
	printLtBounds,
	printLtParameters,
	printMacroRules,
	printMaybeBlockBody,
	printMemberExpression,
	printNumber,
	printObject,
	printParametersAndReturnType,
	printRuleMatch,
	printRuleTransform,
	printTypeAnnotation,
	printTypeArguments,
	printTypeBounds,
	printUnaryExpression,
	printUnionPattern,
} from "./core";
import { DCM, Doc, group, hardline, ifBreak, indent, join, line, softline, willBreak } from "./external";
import { f, getOptions, getParentNode, pathCall, sg_duo, sg_single, type print } from "./plugin";
import { needsParens, stmtNeedsSemi } from "./styling";

type nPrint<T extends Node> = (print: print<T>, node: T) => Doc | never;

export const printer: { [K in NodeType]: nPrint<Extract<NTMap[K], Node>> } = {
	[NodeType.MissingNode](print, node) {
		return "";
	},
	[NodeType.SourceFile](print, node) {
		return [
			print.b("UTF8BOM", "\uFEFF"), //
			print("shebang"),
			print("program"),
		];
	},
	[NodeType.Shebang](print, node) {
		return [`#!${node.value}`, hardline];
	},
	[NodeType.Program](print, node) {
		return printBodyOrCases(print, node);
	},
	[NodeType.Snippet](print, node) {
		exit.never();
	},
	[NodeType.Identifier](print, node) {
		return node.name;
	},
	[NodeType.Index](print, node) {
		return node.name;
	},
	[NodeType.LbIdentifier](print, node) {
		return node.name;
	},
	[NodeType.McIdentifier](print, node) {
		return node.name;
	},
	[NodeType.LtIdentifier](print, node) {
		return node.name;
	},
	[NodeType.PunctuationToken](print, node) {
		return node.token;
	},
	[NodeType.DelimGroup](print, node) {
		return node.loc.getOwnText();
	},
	[NodeType.Literal](print, node) {
		let { value } = node;
		if (is_LiteralNumberLike(node)) value = printNumber(value);
		return hasSuffix(node) ? [value, print("suffix")] : value;
	},
	[NodeType.ItemPath](print, node) {
		return [print("namespace"), "::", print("segment")];
	},
	[NodeType.ExpressionPath](print, node) {
		return [print("namespace"), "::", print("segment")];
	},
	[NodeType.TypePath](print, node) {
		return [print("namespace"), "::", print("segment")];
	},
	[NodeType.Comment](print, node) {
		return print_comment(node);
	},
	[NodeType.DocCommentAttribute](print, node) {
		return print_comment(node);
	},
	[NodeType.Attribute](print, node) {
		return [
			node.inner ? "#![" : "#[",
			isTransformed(node)
				? [print("segments"), printDanglingCommentsForInline(node)] //
				: node.segments.loc.sliceText(1, -1).trim(),
			"]",
		];
	},
	[NodeType.MacroInvocation](print, node) {
		const hasCurlyBrackets = node.segments.dk === DelimKind["{}"];
		const delim = getDelimChars(node.segments);
		if (node.segments.length === 0) {
			return [print("callee"), "!", hasCurlyBrackets ? " " : "", delim.left, printDanglingCommentsForInline(node), delim.right];
		}
		if (isTransformed(node)) {
			if (is_CallLikeMacroInvocation(node)) {
				return [print("callee"), "!", printCallArguments(print as print<CallLikeMacroInvocation>, node)];
			}
			if (is_BlockLikeMacroInvocation(node)) {
				return [print("callee"), "!", " ", printBlockBody(print as print<BlockLikeMacroInvocation>, node)];
			}
		}
		let content = node.segments.loc.sliceText(1, -1);
		if (content.trim().length === 0) {
			content = "";
		} else if (!content.includes("\n")) {
			content = content.trim();
			if (hasCurlyBrackets) content = " " + content + " ";
		}
		return [print("callee"), "!", hasCurlyBrackets ? " " : "", delim.left, content, delim.right];
	},
	[NodeType.MacroRulesDeclaration](print, node) {
		return ["macro_rules! ", print("id"), printMacroRules(print, node)];
	},
	[NodeType.MacroRuleDeclaration](print, node) {
		return [printRuleMatch(print, node), " => ", printRuleTransform(print, node), ";"];
	},
	[NodeType.MacroDeclaration](print, node) {
		return [print("pub"), "macro ", print("id"), printMacroRules(print, node)];
	},
	[NodeType.MacroInlineRuleDeclaration](print, node) {
		return [printRuleMatch(print, node), " ", printRuleTransform(print, node)];
	},
	[NodeType.MacroGroup](print, node) {
		return node.loc.getOwnText();
	},
	[NodeType.MacroParameterDeclaration](print, node) {
		return [print("id"), ":", print("ty")];
	},
	[NodeType.PubSpecifier](print, node) {
		if (!node.location) return "pub ";
		if (is_Identifier(node.location)) {
			switch (node.location.name) {
				case "crate":
					if (start(node) === start(node.location)) {
						return "crate ";
					} else {
						return ["pub(", print("location"), ") "];
					}
				case "self":
				case "super":
					return ["pub(", print("location"), ") "];
			}
		}
		return ["pub(in ", print("location"), ") "];
	},
	[NodeType.ExternSpecifier](print, node) {
		return ["extern ", f`${print("abi")} `];
	},
	[NodeType.ExpressionStatement](print, node) {
		return [print("expression"), stmtNeedsSemi(node) ? ";" : ""];
	},
	[NodeType.UseStatement](print, node) {
		return [print("pub"), "use ", print("import"), ";"];
	},
	[NodeType.DestructuredImport](print, node) {
		if (node.specifiers.length === 0) return [print("source"), "::{", printDanglingCommentsForInline(node, DCM["specifiers"]), "}"];
		let space = true;
		__DEV__: if (globalThis.TESTS_FORMAT_DEV) space = false;
		return [
			print("source"),
			group([
				"::{",
				indent([space ? line : softline, join([",", line], print("specifiers")), ifBreak(",")]),
				space ? line : softline,
				"}",
			]),
		];
	},
	[NodeType.AmbientImport](print, node) {
		return f`${print("source")}::*` || "*";
	},
	[NodeType.AnonymousImport](print, node) {
		return [print("source"), " as ", "_"];
	},
	[NodeType.NamedImport](print, node) {
		return [print("source"), f` as ${print("local")}`];
	},
	[NodeType.ExternCrateStatement](print, node) {
		return [print("pub"), "extern crate ", print("import"), ";"];
	},
	[NodeType.TypeAliasDeclaration](print, node) {
		return [
			print("pub"),
			"type",
			printAssignment(
				printGenerics_x_whereBounds(print, node, printDeclarationTypeBounds(print, node, ":")), //
				" =",
				"typeExpression"
			),
			";",
		];
	},
	[NodeType.LetVariableDeclaration](print, node) {
		return [
			"let ",
			printAssignment(
				printAnnotatedPattern(print, node), //
				" =",
				"expression"
			),
			f` else ${print("else")}`,
			";",
		];
	},
	[NodeType.ConstVariableDeclaration](print, node) {
		return [
			print("pub"),
			"const ",
			printAssignment(
				printAnnotatedPattern(print, node), //
				" =",
				"expression"
			),
			";",
		];
	},
	[NodeType.StaticVariableDeclaration](print, node) {
		return [
			print("pub"),
			"static ",
			printAssignment(
				printAnnotatedPattern(print, node), //
				" =",
				"expression"
			),
			";",
		];
	},
	[NodeType.ModuleDeclaration](print, node) {
		return [
			print("pub"), //
			print.b("unsafe"),
			"mod ",
			print("id"),
			printMaybeBlockBody(print, node),
		];
	},
	[NodeType.ExternBlockDeclaration](print, node) {
		return [
			print("pub"), //
			print.b("unsafe"),
			"extern ",
			f`${print("abi")} `,
			printBlockBody(print, node),
		];
	},
	[NodeType.FunctionDeclaration](print, node) {
		return [
			print("pub"),
			print.b("const"),
			print.b("async"),
			print.b("unsafe"),
			print("extern"),
			"fn",
			printGenerics_x_whereBounds(print, node, printParametersAndReturnType(node)),
			printMaybeBlockBody(print, node),
		];
	},
	[NodeType.FunctionSelfParameterDeclaration](print, node) {
		return group([print.b("ref", "&"), f`${print("lt")} `, print.b("mut"), "self", printTypeAnnotation(print, node)]);
	},
	[NodeType.FunctionParameterDeclaration](print, node) {
		return group(printAnnotatedPattern(print, node));
	},
	[NodeType.FunctionSpread](print, node) {
		return "...";
	},
	[NodeType.StructDeclaration](print, node) {
		return [print("pub"), "struct", printGenerics_x_whereBounds(print, node, ""), printObject(print, node)];
	},
	[NodeType.StructPropertyDeclaration](print, node) {
		return [print("pub"), print("id"), printTypeAnnotation(print, node)];
	},
	[NodeType.TupleStructDeclaration](print, node) {
		return [print("pub"), "struct", printGenerics_x_whereBounds(print, node, printArrayLike(print, node)), ";"];
	},
	[NodeType.TupleStructItemDeclaration](print, node) {
		return [print("pub"), print("typeAnnotation")];
	},
	[NodeType.UnionDeclaration](print, node) {
		return [print("pub"), "union", printGenerics_x_whereBounds(print, node, ""), printObject(print, node)];
	},
	[NodeType.EnumDeclaration](print, node) {
		return [print("pub"), "enum", printGenerics_x_whereBounds(print, node, ""), printEnumBody(print, node)];
	},
	[NodeType.EnumMemberDeclaration](print, node) {
		return [
			print("pub"),
			printAssignment(
				print("id"), //
				" =",
				"value"
			),
		];
	},
	[NodeType.EnumMemberTupleDeclaration](print, node) {
		return [
			print("pub"),
			printAssignment(
				[print("id"), printArrayLike(print, node)], //
				" =",
				"value"
			),
		];
	},
	[NodeType.EnumMemberStructDeclaration](print, node) {
		return [
			print("pub"),
			printAssignment(
				[print("id"), printObject(print, node)], //
				" =",
				"value"
			),
		];
	},
	[NodeType.TraitDeclaration](print, node) {
		return [
			print("pub"),
			print.b("unsafe"),
			"trait",
			printGenerics_x_whereBounds(print, node, printDeclarationTypeBounds(print, node, ":")),
			adjustClause(node, printBlockBody(print, node)),
		];
	},
	[NodeType.AutoTraitDeclaration](print, node) {
		return [
			print("pub"),
			print.b("unsafe"),
			"auto trait ",
			print("id"),
			" ",
			printBlockBody(print, node as any), // see "transform.ts"
		];
	},
	[NodeType.TraitAliasDeclaration](print, node) {
		return [
			print("pub"),
			print.b("unsafe"),
			"trait",
			printGenerics_x_whereBounds(print, node, printDeclarationTypeBounds(print, node, " =")),
			";",
		];
	},
	[NodeType.ImplDeclaration](print, node) {
		return [
			print("pub"),
			print.b("unsafe"),
			"impl",
			printGenerics_x_whereBounds(print, node, [print.b("const"), printImplTraitForType(print, node)]),
			adjustClause(node, printBlockBody(print, node)),
		];
	},
	[NodeType.NegativeImplDeclaration](print, node) {
		return [
			print("pub"),
			"impl",
			printGenerics_x_whereBounds(print, node, ["!", printImplTraitForType(print, node)]),
			" ",
			printBlockBody(print, node as any), // see "transform.ts"
		];
	},
	[NodeType.ExpressionTypeSelector](print, node) {
		return group(["<", print("typeTarget"), f` as ${print("typeExpression")}`, ">"]);
	},
	[NodeType.ExpressionTypeCast](print, node) {
		return [print("typeCallee"), f`::${printTypeArguments(print, node)}`];
	},
	[NodeType.ExpressionAsTypeCast](print, node) {
		return [print("expression"), " as ", print("typeExpression")];
	},
	[NodeType.ReturnExpression](print, node) {
		return ["return", printFlowControlExpression(print, node)];
	},
	[NodeType.BreakExpression](print, node) {
		return ["break", f` ${print("label")}`, printFlowControlExpression(print, node)];
	},
	[NodeType.ContinueExpression](print, node) {
		return ["continue", f` ${print("label")}`];
	},
	[NodeType.YieldExpression](print, node) {
		return ["yield", printFlowControlExpression(print, node)];
	},
	[NodeType.RangeLiteral](print, node) {
		return [print("lower"), "..", print.b("last", "="), print("upper")];
	},
	[NodeType.CallExpression](print, node) {
		return printCallExpression(print, node);
	},
	[NodeType.MemberExpression](print, node) {
		return printMemberExpression(print, node);
	},
	[NodeType.AwaitExpression](print, node) {
		return [print("expression"), ".await"];
	},
	[NodeType.UnwrapExpression](print, node) {
		return [print("expression"), "?"];
	},
	[NodeType.ParenthesizedExpression](print, node) {
		exit.never();
		const shouldHug = !hasComment(node.expression) && (is_ArrayOrTupleLiteral(node.expression) || is_StructLiteral(node.expression));
		if (shouldHug) return ["(", print("expression"), ")"];
		return group(["(", indent([softline, print("expression")]), softline, ")"]);
	},
	[NodeType.MinusExpression](print, node) {
		return printUnaryExpression("-", node);
	},
	[NodeType.NotExpression](print, node) {
		return printUnaryExpression("!", node);
	},
	[NodeType.OrExpression](print, node) {
		return printBinaryishExpression(print, node);
	},
	[NodeType.AndExpression](print, node) {
		return printBinaryishExpression(print, node);
	},
	[NodeType.ReassignmentExpression](print, node) {
		return printAssignment(print("left"), " =", "right");
	},
	[NodeType.UnassignedExpression](print, node) {
		return "_";
	},
	[NodeType.OperationExpression](print, node) {
		return printBinaryishExpression(print, node);
	},
	[NodeType.ReassignmentOperationExpression](print, node) {
		return printAssignment(print("left"), " " + node.kind, "right");
	},
	[NodeType.ComparisonExpression](print, node) {
		return printBinaryishExpression(print, node);
	},
	[NodeType.LetScrutinee](print, node) {
		return ["let ", printAssignment(print("pattern"), " =", "expression")];
	},
	[NodeType.ClosureFunctionExpression](print, node) {
		return printArrowFunction(print, node);
	},
	[NodeType.ClosureFunctionParameterDeclaration](print, node) {
		return group(printAnnotatedPattern(print, node));
	},
	[NodeType.BlockExpression](print, node) {
		return [
			f`${print("label")}: `,
			print.b("const"),
			print.b("async"),
			print.b("move"),
			print.b("unsafe"),
			printBlockBody(print, node),
		];
	},
	[NodeType.LoopBlockExpression](print, node) {
		return [f`${print("label")}: `, "loop ", printBlockBody(print, node)];
	},
	[NodeType.WhileBlockExpression](print, node) {
		return [f`${print("label")}: `, "while ", printCondition(print, node), printBlockBody(print, node)];
	},
	[NodeType.ForInBlockExpression](print, node) {
		return [f`${print("label")}: `, "for ", print("pattern"), " in ", print("expression"), " ", printBlockBody(print, node)];
	},
	[NodeType.IfBlockExpression](print, node) {
		return [f`${print("label")}: `, printIfBlock(print, node)];
	},
	[NodeType.TryBlockExpression](print, node) {
		return [f`${print("label")}: `, "try ", printBlockBody(print, node)];
	},
	[NodeType.MatchExpression](print, node) {
		const id = Symbol("match");
		const expr = print("expression");
		const needs_parens = pathCall(node, "expression", needsParens);

		let printed: Doc = [
			f`${print("label")}: `,
			"match ",
			needs_parens ? expr : group([indent([softline, expr]), softline], { id }),
			needs_parens ? " " : !willBreak(expr) ? ifBreak("", " ", { groupId: id }) : "" /*  ifBreak("", " ", { groupId: id }) */,
			printBlockBody(print, node),
		];

		const parent = getParentNode()!;
		if (is_ClosureFunctionExpression(parent) && parent.expression === node) {
			printed = parenthesize_if_break([indent([softline, printed]), softline]);
		}
		return printed;
	},
	[NodeType.MatchExpressionCase](print, node) {
		return group([
			group(print("pattern")),
			" ",
			printIfBlockCondition(print, node),
			"=>", //
			(is_BlockExpression(node.expression) || is_IfBlockExpression(node.expression)) &&
			!hasComment(node.expression, 0, (comment) => getOptions().danglingAttributes.includes(comment as any))
				? [" ", print("expression")]
				: group(indent([line, print("expression")])),
		]);
		return printAssignment(
			[print("pattern"), " ", printIfBlockCondition(print, node)], //
			"=>",
			"expression"
		);
		return [print("pattern"), " ", printIfBlockCondition(print, node)];
	},
	[NodeType.StructLiteral](print, node) {
		return [print("struct"), printObject(print, node)];
	},
	[NodeType.StructLiteralPropertyShorthand](print, node) {
		return print("value");
	},
	[NodeType.StructLiteralProperty](print, node) {
		return [print("key"), ": ", print("value")];
	},
	[NodeType.StructLiteralPropertySpread](print, node) {
		return ["..", print("expression")];
	},
	[NodeType.StructLiteralRestUnassigned](print, node) {
		return "..";
	},
	[NodeType.ArrayLiteral](print, node) {
		return printArrayLike(print, node);
	},
	[NodeType.SizedArrayLiteral](print, node) {
		return sg_duo`[${print("initExpression")};${print("sizeExpression")}]`;
	},
	[NodeType.TupleLiteral](print, node) {
		return printArrayLike(print, node);
	},
	[NodeType.ReferenceExpression](print, node) {
		return printUnaryExpression(["&", print.b("mut")], node);
	},
	[NodeType.RawReferenceExpression](print, node) {
		return printUnaryExpression(`&raw ${node.kind} `, node);
	},
	[NodeType.DereferenceExpression](print, node) {
		return printUnaryExpression("*", node);
	},
	[NodeType.BoxExpression](print, node) {
		return printUnaryExpression("box ", node);
	},
	[NodeType.UnionPattern](print, node) {
		return printUnionPattern(print, node);
	},
	[NodeType.ParenthesizedPattern](print, node) {
		exit.never();
		return sg_single`(${print("pattern")})`;
	},
	[NodeType.RestPattern](print, node) {
		return "..";
	},
	[NodeType.WildcardPattern](print, node) {
		return "_";
	},
	[NodeType.PatternVariableDeclaration](print, node) {
		return [print.b("ref"), print.b("mut"), printAssignment(print("id"), " @", "pattern")];
	},
	[NodeType.StructPattern](print, node) {
		return [print("struct"), printObject(print, node)];
	},
	[NodeType.StructPatternPropertyDestructured](print, node) {
		return [print("key"), ": ", print("pattern")];
	},
	[NodeType.StructPatternPropertyShorthand](print, node) {
		return [print.b("box"), print.b("ref"), print.b("mut"), print("id")];
	},
	[NodeType.TuplePattern](print, node) {
		return [print("struct"), printArrayLike(print, node)];
	},
	[NodeType.ArrayPattern](print, node) {
		return printArrayLike(print, node);
	},
	[NodeType.ReferencePattern](print, node) {
		return ["&", print.b("mut"), print("pattern")];
	},
	[NodeType.BoxPattern](print, node) {
		return ["box ", print("pattern")];
	},
	[NodeType.MinusPattern](print, node) {
		return ["-", print("pattern")];
	},
	[NodeType.RangePattern](print, node) {
		return [print("lower"), "..", print.b("last", "="), print("upper")];
	},
	[NodeType.TypeCall](print, node) {
		return [print("typeCallee"), printTypeArguments(print, node)];
	},
	[NodeType.TypeCallNamedArgument](print, node) {
		return printAssignment(print("target"), " =", "typeExpression");
	},
	[NodeType.TypeCallNamedBound](print, node) {
		return [print("typeTarget"), printTypeBounds(":", print, node)];
	},
	[NodeType.LtElided](print, node) {
		return "'_";
	},
	[NodeType.LtStatic](print, node) {
		return "'static";
	},
	[NodeType.TypeNever](print, node) {
		return "!";
	},
	[NodeType.TypeInferred](print, node) {
		return "_";
	},
	[NodeType.GenericTypeParameterDeclaration](print, node) {
		return printAssignment(
			[print("id"), printTypeBounds(":", print, node)], //
			" =",
			"typeDefault"
		);
	},
	[NodeType.ConstTypeParameterDeclaration](print, node) {
		return [
			"const ",
			printAssignment(
				[print("id"), printTypeAnnotation(print, node)], //
				" =",
				"typeDefault"
			),
		];
	},
	[NodeType.GenericLtParameterDeclaration](print, node) {
		return [print("id"), printLtBounds(":", print, node)];
	},
	[NodeType.WhereTypeBoundDeclaration](print, node) {
		return [printLtParameters(print, node), print("typeTarget"), printTypeBounds(":", print, node)];
	},
	[NodeType.WhereLtBoundDeclaration](print, node) {
		return [print("ltTarget"), printLtBounds(":", print, node)];
	},
	[NodeType.TypeTraitBound](print, node) {
		return [print.b("maybeConst", "~const "), print.b("optional", "?"), printLtParameters(print, node), print("typeExpression")];
	},
	[NodeType.TypeDynBounds](print, node) {
		return printTypeBounds("dyn", print, node);
	},
	[NodeType.TypeImplBounds](print, node) {
		return printTypeBounds("impl", print, node);
	},
	[NodeType.TypeFnPointer](print, node) {
		return [printLtParameters(print, node), print.b("unsafe"), print("extern"), "fn", printParametersAndReturnType(node)];
	},
	[NodeType.TypeFnPointerParameter](print, node) {
		return [f`${print("id")}: `, print("typeAnnotation")];
	},
	[NodeType.TypeFunction](print, node) {
		return [print("callee"), printParametersAndReturnType(node)];
	},
	[NodeType.TypeTuple](print, node) {
		return printArrayLike(print, node);
	},
	[NodeType.TypeSizedArray](print, node) {
		return sg_duo`[${print("typeExpression")};${print("sizeExpression")}]`;
		if (isSimpleType(node)) return ["[", print("typeExpression"), "; ", print("sizeExpression"), "]"];
	},
	[NodeType.TypeSlice](print, node) {
		if (isSimpleType(node)) return ["[", print("typeExpression"), "]"];
		return sg_single`[${print("typeExpression")}]`;
	},
	[NodeType.TypeReference](print, node) {
		return ["&", f`${print("lt")} `, print.b("mut"), print("typeExpression")];
	},
	[NodeType.TypeDereferenceConst](print, node) {
		return ["*const ", print("typeExpression")];
	},
	[NodeType.TypeDereferenceMut](print, node) {
		return ["*mut ", print("typeExpression")];
	},
	[NodeType.TypeParenthesized](print, node) {
		exit.never();
		return sg_single`(${print("typeExpression")})`;
	},
};


================================================
FILE: src/format/styling.ts
================================================
import {
	ClosureFunctionExpression,
	ComparisonExpression,
	ConditionExpression,
	EnumDeclaration,
	EnumMemberStructDeclaration,
	ExpressionAsTypeCast,
	ExpressionNode,
	ExpressionStatement,
	ExpressionWithBody,
	LeftRightExpression,
	LogicalExpression,
	MacroDeclaration,
	MacroRulesDeclaration,
	MissingNode,
	Node,
	NodeType,
	NodeWithBody,
	NodeWithBodyOrCases,
	OperationExpression,
	PRCD,
	StructDeclaration,
	StructLiteral,
	StructPattern,
	TK,
	UnionDeclaration,
} from "jinx-rust";
import {
	can_have_OuterAttributes,
	getAstPath,
	getPrecedence,
	hasAttributes,
	hasBody,
	hasCondition,
	hasItems,
	hasLetScrutineeCondition,
	hasOuterAttributes,
	hasProperties,
	is_Attribute,
	is_AttributeOrDocComment,
	is_AwaitExpression,
	is_BitwiseOperator,
	is_CallExpression,
	is_ClosureFunctionExpression,
	is_ComparisonExpression,
	is_DocCommentAttribute,
	is_ElseBlock,
	is_EnumMemberDeclaration,
	is_EqualityOperator,
	is_ExpressionAsTypeCast,
	is_ExpressionStatement,
	is_ExpressionWithBody,
	is_ExpressionWithBodyOrCases,
	is_ExpressionWithBodyOrCases_or_BlockLikeMacroInvocation,
	is_FlowControlExpression,
	is_FlowControlMaybeValueExpression,
	is_ForInBlockExpression,
	is_Identifier,
	is_IfBlockExpression,
	is_ImplicitReturnAbleNode,
	is_LeftRightExpression,
	is_LetScrutinee,
	is_Literal,
	is_LiteralNumberLike,
	is_LogicalExpression,
	is_LoopBlockExpression,
	is_MatchExpression,
	is_MatchExpressionCase,
	is_MemberExpression,
	is_NodeWithBodyNoBody,
	is_NodeWithMaybePatternNoUnionBody,
	is_OperationExpression,
	is_ParenthesizedNode,
	is_PatternVariableDeclaration,
	is_PostfixExpression,
	is_RangeLiteral,
	is_ReassignmentNode,
	is_ReturnExpression,
	is_StatementNode,
	is_StructLiteral,
	is_StructLiteralProperty,
	is_StructPatternProperty,
	is_StructPropertyDeclaration,
	is_TypeBoundsStandaloneNode,
	is_TypeFunctionNode,
	is_TypeTraitBound,
	is_UnaryExpression,
	is_UnaryType,
	is_UnionPattern,
	is_WhileBlockExpression,
	is_YieldExpression,
	is_bitshiftOperator,
	is_multiplicativeOperator,
} from "jinx-rust/utils";
import { BlockLikeMacroInvocation, is_BlockLikeMacroInvocation, is_CallExpression_or_CallLikeMacroInvocation } from "../transform";
import { exit, last_of } from "../utils/common";
import { CF, hasBreaklineAfter, hasComment } from "./comments";
import { flowControlExpressionNeedsOuterParens } from "./core";
import { Doc, hardline, softline, willBreak } from "./external";
import {
	assertPathAtNode,
	getContext,
	getGrandParentNode,
	getNode,
	getOptions,
	getParentNode,
	getPrintFn,
	is_printing_macro,
	pathCallAtParent,
	pathCallParentOf,
	stackIncludes,
} from "./plugin";

export function needsOuterSoftbreakParens(node: Node) {
	const parent = getParentNode(node);

	if (!parent) return false;

	if (is_ExpressionAsTypeCast(node)) {
		return precedenceNeedsParens(node, parent);
	}

	if (
		is_FlowControlMaybeValueExpression(parent) && //
		parent.expression === node &&
		flowControlExpressionNeedsOuterParens(parent)
	) {
		return true;
	}
	if (
		is_ExpressionWithBodyOrCases_or_BlockLikeMacroInvocation(node) &&
		(false ||
			(is_MemberExpression(parent) && parent.expression === node) ||
			(is_ExpressionWithBodyOrCases_or_BlockLikeMacroInvocation(parent) && !is_ElseBlock(node, parent)))
	) {
		return true;
	}

	if (is_UnionPattern(node) && is_NodeWithMaybePatternNoUnionBody(parent)) {
		return true;
	}

	if (hasComment(node)) {
		if (is_UnaryExpression(parent)) {
			return true;
		}

		if (hasComment(node, CF.Line)) {
			if (is_ReturnExpression(parent) || (is_YieldExpression(parent) && parent.expression === node)) {
				return true;
			}
		}

		if (
			hasComment(node, CF.Leading, (comment) => is_Attribute(comment) && !comment.inner) &&
			!can_have_OuterAttributes(node, parent, true)
		) {
			return true;
		}
	}

	return false;
}

export function needsInnerParens(node: Node) {
	if (needsOuterSoftbreakParens(node)) {
		return false;
	}

	const parent = getParentNode(node);

	if (!parent) {
		return false;
	}

	if (is_Identifier(node)) {
		return false;
	}

	if (is_Literal(node)) {
		return is_LiteralNumberLike(node) && is_MemberExpression(parent) && node === parent.expression;
	}

	if (is_CallExpression(parent) && parent.callee === node && is_MemberExpression(node)) {
		return !getOptions().actuallyMethodNodes.has(node);
	}

	if (is_ReassignmentNode(node)) {
		if (is_printing_macro()) {
			return false;
		}

		if (is_ClosureFunctionExpression(parent) && node === parent.expression) {
			return true;
		}

		if (is_ExpressionStatement(parent)) {
			return is_StructLiteral(node.left);
		}

		if (is_ReassignmentNode(parent)) {
			return false;
		}

		return true;
	}

	if (is_ParenthesizedNode(parent)) {
		return false;
	}

	if (is_ExpressionStatement(parent)) {
		return false;
	}

	if (is_RangeLiteral(node)) {
		return (
			is_ExpressionAsTypeCast(parent) ||
			is_LogicalExpression(parent) ||
			is_UnaryExpression(parent) ||
			is_PostfixExpression(parent) ||
			(is_MemberExpression(parent) && node === parent.expression) ||
			(is_CallExpression(parent) && node === parent.callee) ||
			is_OperationExpression(parent) ||
			is_ComparisonExpression(parent)
		);
	}

	if (is_LetScrutinee(parent) && is_LogicalExpression(node) && parent.expression === (node as any)) {
		return true;
	}

	if (is_UnaryExpression(node)) {
		switch (parent.nodeType) {
			case NodeType.MemberExpression:
			case NodeType.AwaitExpression:
				return node === parent.expression;
			case NodeType.CallExpression:
				return node === parent.callee;
			default:
				return false;
		}
	}

	if (is_ExpressionWithBodyOrCases_or_BlockLikeMacroInvocation(node)) {
		if (is_ExpressionWithBodyOrCases(parent)) {
			return !is_ElseBlock(node, parent);
		}
		if (is_LetScrutinee(parent) && parent.expression === node && is_ExpressionWithBodyOrCases(getGrandParentNode())) {
			return true;
		}
		return (
			is_ExpressionAsTypeCast(parent) ||
			is_LogicalExpression(parent) ||
			is_UnaryExpression(parent) ||
			is_PostfixExpression(parent) ||
			(is_MemberExpression(parent) && node === parent.expression) ||
			(is_CallExpression(parent) && node === parent.callee) ||
			is_OperationExpression(parent) ||
			is_ComparisonExpression(parent) ||
			is_RangeLiteral(parent)
		);
	}

	if (is_StructLiteral(node)) {
		if (is_ExpressionWithBodyOrCases(parent)) {
			return true;
		}

		if (is_LetScrutinee(parent) && parent.expression === node && is_ExpressionWithBodyOrCases(getGrandParentNode())) {
			return true;
		}
		if (is_UnaryExpression(parent) || is_PostfixExpression(parent) || is_MemberExpression(parent)) {
			return parent.expression === node;
		}
		if (is_CallExpression(parent)) {
			return parent.callee === node;
		}
	}

	if (is_LogicalExpression(node) || is_OperationExpression(node) || is_ComparisonExpression(node) || is_ClosureFunctionExpression(node)) {
		return precedenceNeedsParens(node, parent);
	}

	if (is_TypeFunctionNode(node)) {
		const gp = getGrandParentNode();
		if (node.returnType && is_TypeTraitBound(parent) && is_TypeBoundsStandaloneNode(gp) && last_of(gp.typeBounds) !== parent) {
			return true;
		}
	}

	if (is_TypeBoundsStandaloneNode(node)) {
		return (
			(is_UnaryType(parent) && node.typeBounds.length > 1) ||
			is_TypeBoundsStandaloneNode(parent) ||
			is_TypeTraitBound(parent) ||
			(is_TypeFunctionNode(parent) && parent.returnType === node)
		);
	}

	if (is_PatternVariableDeclaration(parent)) {
		return is_UnionPattern(node);
	}

	return false;
}

function precedenceNeedsParens(node: LeftRightExpression | ClosureFunctionExpression | ExpressionAsTypeCast, parent: Node) {
	if (is_UnaryExpression(parent) || is_PostfixExpression(parent)) return true;
	if (is_ReassignmentNode(parent)) return parent.left === node;
	if (is_MemberExpression(parent)) return parent.expression === node;
	if (is_CallExpression(parent)) return parent.callee === node;
	if (is_ExpressionAsTypeCast(parent)) return !is_ExpressionAsTypeCast(node);
	if (is_LogicalExpression(parent)) return is_LogicalExpression(node) ? parent.nodeType !== node.nodeType : evalPrecedence(node, parent);
	if (is_OperationExpression(parent) || is_ComparisonExpression(parent)) return evalPrecedence(node, parent);
	return false;
	function evalPrecedence(
		child: LeftRightExpression | ClosureFunctionExpression | ExpressionAsTypeCast,
		parent: ComparisonExpression | OperationExpression | LogicalExpression
	) {
		if (is_ExpressionAsTypeCast(child) || is_ClosureFunctionExpression(child)) {
			return true;
		}
		function getPrec(node, bool) {
			// if (is_EqualityOperator(node.tk)) {
			// 	return 11.3;
			// }
			// if (is_LargerLesserOperator(node.tk)) {
			// 	return 11.6;
			// }
			return getPrecedence(node, bool);
		}

		const childPRCD = getPrec(child, is_insideScrutinee(child));
		const parentPRCD = getPrec(parent, is_insideScrutinee(parent));

		if (parentPRCD > childPRCD) {
			return true;
		}

		if (parentPRCD === childPRCD && parent.right === child) {
			return true;
		}

		if (parentPRCD === childPRCD && !shouldFlatten(parent, child)) {
			return true;
		}

		if (parentPRCD < childPRCD && child.tk === TK["%"]) {
			return parentPRCD === PRCD["+-"];
		}

		if (is_BitwiseOperator(parent.tk) || (is_BitwiseOperator(child.tk) && is_EqualityOperator(parent.tk))) {
			return true;
		}

		return false;
	}
}

export function shouldFlatten(parent: ExpressionNode | ConditionExpression, node: ExpressionNode | ConditionExpression) {
	if (getPrecedence(node, is_insideScrutinee(node)) !== getPrecedence(parent, is_insideScrutinee(parent))) return false;
	if (is_ComparisonExpression(parent) && is_ComparisonExpression(node)) return false;
	if (is_OperationExpression(parent) && is_OperationExpression(node)) {
		if (
			(node.tk === TK["%"] && is_multiplicativeOperator(parent.tk)) ||
			(parent.tk === TK["%"] && is_multiplicativeOperator(node.tk)) ||
			(node.tk !== parent.tk && is_multiplicativeOperator(node.tk) && is_multiplicativeOperator(parent.tk)) ||
			(is_bitshiftOperator(node.tk) && is_bitshiftOperator(parent.tk))
		)
			return false;
	}
	return true;
}

export function needsParens(node: Node) {
	return needsOuterSoftbreakParens(node) || needsInnerParens(node);
}

export function stmtNeedsSemi(stmt: ExpressionStatement, disregardExprType = false) {
	return pathCallParentOf(stmt, (parent) => needsSemi(parent as any, stmt, disregardExprType));
}

const NoNode = { nodeType: 0 } as MissingNode;

export function needsSemi(parent: NodeWithBody, stmt: ExpressionStatement, disregardExprType = false) {
	const expr = disregardExprType ? NoNode : stmt.expression!;
	const hadSemi = !disregardExprType && stmt.semi;

	return (
		!!expr &&
		(forcePreserveSemi()
			? true
			: shouldNeverSemi()
			? false
			: shouldPreserveSemi()
			? hadSemi || shouldAlwaysSemi() || canAutoCompleteSemi()
			: true)
	);

	function forcePreserveSemi() {
		/** Rust Compiler bug (preserve optional semicolon) */
		// rust-lang/rust#70844 https://github.com/rust-lang/rust/issues/70844
		// issue#22 https://github.com/jinxdash/prettier-plugin-rust/issues/22
		return (
			hadSemi &&
			stmt === last_of(parent.body!) &&
			((is_IfBlockExpression(expr) &&
				hasLetScrutineeCondition(expr) &&
				!(is_LetScrutinee(expr.condition) && is_Identifier(expr.condition.expression))) ||
				(is_MatchExpression(expr) && !is_Identifier(expr.expression)))
		);
	}
	function shouldNeverSemi() {
		return is_ExpressionWithBodyOrCases_or_BlockLikeMacroInvocation(expr);
	}
	function shouldPreserveSemi() {
		return stmt === last_of(parent.body!) && (is_ImplicitReturnAbleNode(parent) || is_BlockLikeMacroInvocation(parent));
	}
	function shouldAlwaysSemi() {
		return is_FlowControlExpression(expr) || is_ReassignmentNode(expr);
	}
	function canAutoCompleteSemi() {
		return withPathAt(parent, function checkParent(child: NodeWithBodyOrCases): boolean {
			return pathCallParentOf(child, (parent) => {
				if (is_IfBlockExpression(parent) && parent.else === child) {
					// if ... { ... } else if { ... } ...
					// ^ ------------------------------- parent
					//                     ^ ----------- child
					return checkParent(parent);
				}
				if (is_ExpressionStatement(parent)) {
					// { .... { ... } ... }
					// ^ -----------------^ grandparent
					//        ^ --- ^       ExpressionStatement<child>
					if (hasOuterAttributes(parent)) return false;
					return stmtNeedsSemi(parent, true);
				}
				if (is_MatchExpressionCase(parent) && parent.expression === child) {
					return pathCallParentOf(parent, checkParent);
				}
				return false;
			});
		});
	}
}

export function canInlineBlockBody(node: NodeWithBodyOrCases | BlockLikeMacroInvocation): boolean {
	if (!is_ExpressionWithBody(node)) {
		return false;
	}
	const body = node.body;

	if (body.length === 0) {
		return canInlineInlineable(node);
	}

	if (body.length === 1) {
		const stmt = body[0];
		if (is_AttributeOrDocComment(stmt)) {
			return true;
		}
		if (is_ExpressionStatement(stmt) && !needsSemi(node, stmt)) {
			/**
			 * parent ( ExpressionStatement | StructLiteralProperty | LetVariableDeclaration | ... )
			 *   ...
			 *   node {
			 *     expr
			 *   }
			 *   ...
			 *
			 *
			 * Q: Can you inline "node { expr }" ?
			 */
			const expr = stmt.expression!;

			if (
				is_FlowControlExpression(expr) || //
				is_ClosureFunctionExpression(expr) ||
				is_ExpressionWithBodyOrCases_or_BlockLikeMacroInvocation(expr)
			) {
				return false;
			}

			return canInlineInlineable(node);
		}
	}
	return false;
}

// function q(node: ExpressionWithBody) {
// 	pathCallTopMostIfBlockExpression(node, (node) => {});
// }

function canInlineInlineable(node: ExpressionWithBody) {
	if (is_ForInBlockExpression(node) || is_LoopBlockExpression(node)) {
		return false;
	}
	if (is_WhileBlockExpression(node)) {
		return true;
	}

	const parent = getParentNode(node)!;

	if (
		is_ExpressionStatement(parent) &&
		(!is_ImplicitReturnAbleNode(node) || pathCallAtParent(parent, (parent) => stmtNeedsSemi(parent, true)))
	) {
		return false;
	}

	if (is_ElseBlock(node, parent)) {
		return pathCallAtParent(parent, canInlineBlockBody);
	}
	// if (is_CaseBlock(node, parent)) {
	// 	return false;
	// }
	if (is_IfBlockExpression(node)) {
		if (
			!node.else ||
			// hasLetScrutineeCondition(node) ||
			is_ExpressionWithBodyOrCases_or_BlockLikeMacroInvocation(node.condition) ||
			willBreak(getPrintFn(node)("condition"))
		) {
			return false;
		}

		const grandpa
Download .txt
gitextract_sexpc5gs/

├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── Bug_report.yml
│       └── config.yml
├── .gitignore
├── .gitmodules
├── .npmrc
├── .vscode/
│   ├── launch.json
│   └── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── crate/
│   ├── Cargo.toml
│   ├── README.md
│   └── src/
│       └── main.rs
├── extension/
│   ├── LICENSE
│   ├── README.md
│   ├── package.json
│   ├── src/
│   │   └── index.ts
│   └── tsconfig.json
├── package.json
├── pnpm-workspace.yaml
├── scripts/
│   ├── build.ts
│   ├── dev.format.ts
│   ├── dev.repl.ts
│   └── tsconfig.json
├── src/
│   ├── format/
│   │   ├── comments.ts
│   │   ├── complexity.ts
│   │   ├── core.ts
│   │   ├── external.ts
│   │   ├── plugin.ts
│   │   ├── printer.ts
│   │   └── styling.ts
│   ├── index.ts
│   ├── transform/
│   │   ├── custom/
│   │   │   ├── attribute.ts
│   │   │   ├── cfg_if.ts
│   │   │   └── utils.ts
│   │   └── index.ts
│   └── utils/
│       ├── common.ts
│       └── debug.ts
├── tests/
│   ├── output/
│   │   ├── comments/
│   │   │   ├── assignment.f.rs
│   │   │   ├── binaryish.f.rs
│   │   │   ├── blocks.f.rs
│   │   │   ├── chain.f.rs
│   │   │   ├── closure.f.rs
│   │   │   ├── dangling.f.rs
│   │   │   ├── file.f.rs
│   │   │   ├── flow.f.rs
│   │   │   ├── functions.f.rs
│   │   │   ├── ignore.attr.f.rs
│   │   │   ├── ignore.f.rs
│   │   │   ├── ignore.file.f.rs
│   │   │   ├── imports.f.rs
│   │   │   ├── macro.f.rs
│   │   │   ├── multiple.f.rs
│   │   │   ├── parens.f.rs
│   │   │   └── whitespace.f.rs
│   │   ├── common/
│   │   │   ├── arrays.f.rs
│   │   │   ├── assignments.f.rs
│   │   │   ├── binaryish.f.rs
│   │   │   ├── chains.f.rs
│   │   │   ├── chains.first-argument-expansion.f.rs
│   │   │   ├── chains.last-argument-expansion.f.rs
│   │   │   ├── closures.f.rs
│   │   │   ├── destructuring.f.rs
│   │   │   ├── members.f.rs
│   │   │   └── types.f.rs
│   │   ├── issues/
│   │   │   ├── 0.f.rs
│   │   │   ├── 14.f.rs
│   │   │   ├── 21/
│   │   │   │   ├── fn_comment.f.rs
│   │   │   │   ├── fn_fn.f.rs
│   │   │   │   ├── fn_ln.f.rs
│   │   │   │   ├── ln_fn_ln.f.rs
│   │   │   │   └── mod.f.rs
│   │   │   ├── 22.f.rs
│   │   │   ├── 25.f.rs
│   │   │   ├── nth-pass.f.1.rs
│   │   │   └── nth-pass.f.rs
│   │   ├── macros/
│   │   │   ├── cfg_if.f.rs
│   │   │   ├── if_chain.f.rs
│   │   │   └── matches.f.rs
│   │   └── styling/
│   │       ├── blockify.f.rs
│   │       ├── canInlineBlockBody.f.rs
│   │       ├── needsParens.f.rs
│   │       └── needsSemi.f.rs
│   ├── output-ext/
│   │   ├── errors/
│   │   │   └── foo.rs
│   │   ├── expressions/
│   │   │   ├── block.f.rs
│   │   │   ├── closure.f.rs
│   │   │   ├── expr.f.rs
│   │   │   ├── flow_expr.f.rs
│   │   │   ├── ident.f.rs
│   │   │   ├── literal.f.rs
│   │   │   ├── match.f.rs
│   │   │   ├── parens.f.rs
│   │   │   ├── precedence.f.rs
│   │   │   └── range.f.rs
│   │   ├── features/
│   │   │   ├── arbitrary_enum_discriminant.f.rs
│   │   │   ├── associated_type_bounds.f.rs
│   │   │   ├── async_closure.f.rs
│   │   │   ├── auto_traits.f.rs
│   │   │   ├── const_generics_defaults.f.rs
│   │   │   ├── const_trait_impl.f.rs
│   │   │   ├── decl_macro.f.rs
│   │   │   ├── destructuring_assignment.f.rs
│   │   │   ├── generators.f.rs
│   │   │   ├── if_let_guard.f.rs
│   │   │   ├── inline_const.f.rs
│   │   │   ├── inline_const_pat.f.rs
│   │   │   ├── let_chains.f.rs
│   │   │   ├── let_else.f.rs
│   │   │   ├── negative_impls.f.rs
│   │   │   └── trait_alias.f.rs
│   │   ├── macro/
│   │   │   ├── attributes.f.rs
│   │   │   ├── macro.invocation.f.rs
│   │   │   ├── macro.item.f.rs
│   │   │   ├── macro.match.f.rs
│   │   │   ├── macro.tokens.f.rs
│   │   │   └── macro.transform.f.rs
│   │   ├── miscellaneous/
│   │   │   ├── ast-program-locs-attr-dangling.f.rs
│   │   │   ├── ast-program-locs-attr.f.rs
│   │   │   ├── ast-program-locs.f.rs
│   │   │   ├── empty-attr-dangling-x.f.rs
│   │   │   ├── empty-attr-dangling.f.rs
│   │   │   ├── empty-attr-x.f.rs
│   │   │   ├── empty-attr.f.rs
│   │   │   ├── empty-comment-block-x.f.rs
│   │   │   ├── empty-comment-block.f.rs
│   │   │   ├── empty-comment-x.f.rs
│   │   │   ├── empty-comment.f.rs
│   │   │   ├── empty-doc-block-x.f.rs
│   │   │   ├── empty-doc-block.f.rs
│   │   │   ├── empty-doc-x.f.rs
│   │   │   ├── empty-doc.f.rs
│   │   │   ├── empty.f.rs
│   │   │   ├── shebang-b.f.rs
│   │   │   └── shebang.f.rs
│   │   ├── patterns/
│   │   │   ├── pattern.f.rs
│   │   │   ├── rest.f.rs
│   │   │   └── union.f.rs
│   │   ├── specifiers/
│   │   │   ├── extern.f.rs
│   │   │   └── pub.f.rs
│   │   ├── statements/
│   │   │   ├── const.f.rs
│   │   │   ├── enum.f.rs
│   │   │   ├── impl.f.rs
│   │   │   ├── self.f.rs
│   │   │   ├── spread.f.rs
│   │   │   ├── statements.f.rs
│   │   │   ├── static.f.rs
│   │   │   ├── struct.f.rs
│   │   │   ├── trait.f.rs
│   │   │   ├── union.f.rs
│   │   │   └── use.f.rs
│   │   └── types/
│   │       ├── cast.f.rs
│   │       ├── never.f.rs
│   │       └── types.f.rs
│   ├── print.ts
│   ├── samples/
│   │   ├── comments/
│   │   │   ├── assignment.rs
│   │   │   ├── binaryish.rs
│   │   │   ├── blocks.rs
│   │   │   ├── chain.rs
│   │   │   ├── closure.rs
│   │   │   ├── dangling.rs
│   │   │   ├── file.rs
│   │   │   ├── flow.rs
│   │   │   ├── functions.rs
│   │   │   ├── ignore.attr.rs
│   │   │   ├── ignore.file.rs
│   │   │   ├── ignore.rs
│   │   │   ├── imports.rs
│   │   │   ├── macro.rs
│   │   │   ├── multiple.rs
│   │   │   ├── parens.rs
│   │   │   └── whitespace.rs
│   │   ├── common/
│   │   │   ├── arrays.rs
│   │   │   ├── assignments.rs
│   │   │   ├── binaryish.rs
│   │   │   ├── chains.first-argument-expansion.rs
│   │   │   ├── chains.last-argument-expansion.rs
│   │   │   ├── chains.rs
│   │   │   ├── closures.rs
│   │   │   ├── destructuring.rs
│   │   │   ├── members.rs
│   │   │   └── types.rs
│   │   ├── issues/
│   │   │   ├── 0.rs
│   │   │   ├── 14.rs
│   │   │   ├── 21/
│   │   │   │   ├── fn_comment.rs
│   │   │   │   ├── fn_fn.rs
│   │   │   │   ├── fn_ln.rs
│   │   │   │   ├── ln_fn_ln.rs
│   │   │   │   └── mod.rs
│   │   │   ├── 22.rs
│   │   │   ├── 25.rs
│   │   │   └── nth-pass.rs
│   │   ├── macros/
│   │   │   ├── cfg_if.rs
│   │   │   ├── if_chain.rs
│   │   │   └── matches.rs
│   │   └── styling/
│   │       ├── blockify.rs
│   │       ├── canInlineBlockBody.rs
│   │       ├── needsParens.rs
│   │       └── needsSemi.rs
│   └── test.build.ts
├── tsconfig.base.json
├── tsconfig.build.json
└── tsconfig.json
Download .txt
SYMBOL INDEX (1506 symbols across 106 files)

FILE: crate/src/main.rs
  function main (line 1) | fn main() {

FILE: extension/src/index.ts
  function activate (line 10) | async function activate(context: ExtensionContext) {
  function format (line 34) | function format(document: TextDocument, config: Config) {
  function tryFormat (line 54) | function tryFormat(doc: TextDocument, config: prettier.Config) {
  function createOutputChannel (line 75) | function createOutputChannel(name: string) {
  function unstyle (line 92) | function unstyle(str: string) {
  function cmd (line 96) | function cmd(filepath: string | undefined, frompath = "") {
  function normPath (line 99) | function normPath(filepath: string) {

FILE: scripts/dev.format.ts
  function format (line 6) | function format(code: string, filepath: string) {

FILE: src/format/comments.ts
  function addCommentHelper (line 56) | function addCommentHelper(node: Node, comment: AnyComment, leading = fal...
  function addLeadingComment (line 62) | function addLeadingComment(node: Node, comment: AnyComment) {
  function addDanglingComment (line 65) | function addDanglingComment(node: Node, comment: AnyComment, marker: DCM) {
  function addTrailingComment (line 69) | function addTrailingComment(node: Node, comment: AnyComment) {
  function setPrettierIgnoreTarget (line 72) | function setPrettierIgnoreTarget(node: Node, comment: AnyComment) {
  function hasComments (line 78) | function hasComments<T extends Node>(node: T): node is NodeWithComments<...
  function printDanglingComments (line 82) | function printDanglingComments(enclosingNode: Node, sameIndent: boolean,...
  function needsHardlineAfterDanglingComment (line 99) | function needsHardlineAfterDanglingComment(node: Node) {
  function setDidPrintComment (line 104) | function setDidPrintComment(comment: AnyComment) {
  function printComment (line 108) | function printComment(comment: AnyComment) {
  function isPreviousLineEmpty (line 115) | function isPreviousLineEmpty(node: Node) {
  function hasBreaklineBefore (line 122) | function hasBreaklineBefore(node: Node) {
  function hasBreaklineAfter (line 126) | function hasBreaklineAfter(node: Node) {
  function printCommentsSeparately (line 130) | function printCommentsSeparately(ignored?: Set<AnyComment>) {
  function getPostLeadingComment (line 187) | function getPostLeadingComment(comment: AnyComment) {
  function withComments (line 199) | function withComments<D extends Doc>(node: Node, printed: D, ignored?: S...
  function getComments (line 206) | function getComments(node: Node, ...args: Parameters<typeof getCommentTe...
  function getFirstComment (line 218) | function getFirstComment(node: Node, flags: CF, fn?: (comment: AnyCommen...
  function escapeComments (line 223) | function escapeComments(flags: number, fn?: (comment: AnyComment) => boo...
  type CF (line 229) | const enum CF {
  function isPrettierIgnoreComment (line 239) | function isPrettierIgnoreComment(comment: AnyComment) {
  function isPrettierIgnoreAttribute (line 242) | function isPrettierIgnoreAttribute(node: Node): node is MutatedAttribute {
  function getCommentTestFunction (line 245) | function getCommentTestFunction(flags: CF, fn?: (comment: AnyComment) =>...
  function hasComment (line 262) | function hasComment(node: Node, flags: number = 0, fn?: (comment: AnyCom...
  function hasNewlineInRange (line 268) | function hasNewlineInRange(leftIndex: number, rightIndex: number) {
  function isNextLineEmpty (line 274) | function isNextLineEmpty(node: Node) {
  function isNextLineEmptyAfterIndex (line 277) | function isNextLineEmptyAfterIndex(index: number | false) {
  function hasNewline (line 293) | function hasNewline(index: number | false, backwards = false) {
  function skipLineComment (line 298) | function skipLineComment(index: number | false) {
  function skipBlockComment (line 305) | function skipBlockComment(index: number | false) {
  function skipNewline (line 324) | function skipNewline(index: number | false, backwards = false) {
  function skipParens (line 338) | function skipParens(index: number | false, backwards = false) {
  function getNextNonSpaceNonCommentCharacterIndex (line 346) | function getNextNonSpaceNonCommentCharacterIndex(node: Node) {
  function getNextNonSpaceNonCommentCharacterIndexWithStartIndex (line 349) | function getNextNonSpaceNonCommentCharacterIndexWithStartIndex(i: number) {
  function getNextNonSpaceNonCommentCharacter (line 362) | function getNextNonSpaceNonCommentCharacter(node: Node) {
  type CommentContext (line 366) | interface CommentContext {
  function handled (line 377) | function handled(comment: AnyComment) {
  function handleCommon (line 380) | function handleCommon(ctx: CommentContext): boolean {
  function handleOwnLineComment (line 475) | function handleOwnLineComment(ctx: CommentContext) {
  function handleEndOfLineComment (line 478) | function handleEndOfLineComment(ctx: CommentContext) {
  function handleRemainingComment (line 502) | function handleRemainingComment(ctx: CommentContext) {
  function handleStructLiteralComments (line 506) | function handleStructLiteralComments({ enclosingNode, followingNode, com...
  function handleVariableDeclaratorComments (line 512) | function handleVariableDeclaratorComments({ enclosingNode, followingNode...
  function handleMixedInOuterAttributeComments (line 534) | function handleMixedInOuterAttributeComments({ precedingNode, enclosingN...
  function handleAttributeComments (line 561) | function handleAttributeComments({ precedingNode, enclosingNode, followi...
  function handleBadComments (line 604) | function handleBadComments({ precedingNode, enclosingNode, followingNode...
  function is_ABI_Comment (line 622) | function is_ABI_Comment({ precedingNode, enclosingNode, comment }: Comme...
  function handleFlowControlComments (line 628) | function handleFlowControlComments({ precedingNode, enclosingNode, follo...
  function handleFunctionComments (line 635) | function handleFunctionComments(ctx: CommentContext) {
  function handleMacroRuleComments (line 675) | function handleMacroRuleComments(ctx: CommentContext) {
  function handleStatementComments (line 694) | function handleStatementComments(ctx: CommentContext) {
  function addCommentToBlock (line 701) | function addCommentToBlock(block: NodeWithBodyOrCases, comment: AnyComme...
  function handleIfBlockExpressionComments (line 711) | function handleIfBlockExpressionComments(ctx: CommentContext) {
  function handleMemberExpressionComments (line 729) | function handleMemberExpressionComments({ comment, precedingNode, enclos...
  function handleDanglingComments (line 739) | function handleDanglingComments({ comment, enclosingNode }: CommentConte...
  function canAttachCommentInLocArray (line 753) | function canAttachCommentInLocArray(arr: LocArray) {
  function isOwnLine (line 757) | function isOwnLine(comment: AnyComment) {
  function isStartOfLine (line 760) | function isStartOfLine(comment: AnyComment) {
  function isEndOfLine (line 763) | function isEndOfLine(comment: AnyComment) {
  function isDangling (line 766) | function isDangling(comment: AnyComment) {
  function isLeading (line 770) | function isLeading(comment: AnyComment) {
  function isTrailing (line 774) | function isTrailing(comment: AnyComment) {
  function print_comment (line 779) | function print_comment(comment: CommentOrDocComment) {

FILE: src/format/complexity.ts
  constant DEPTH (line 32) | let DEPTH = 0;
  constant ANCESTRY (line 33) | const ANCESTRY: Node[] = [];
  constant LONE_SHORT_ARGUMENT_THRESHOLD_RATE (line 34) | const LONE_SHORT_ARGUMENT_THRESHOLD_RATE = 0.25;
  function withCheckContext (line 36) | function withCheckContext<R>(fn: () => R): R {
  function is_short (line 50) | function is_short(str: string) {
  function print (line 53) | function print(target: Node) {
  function IsSimpleFunction (line 66) | function IsSimpleFunction<T extends Node>(fn: (node: T) => boolean): (no...
  function HasComplexFunction (line 84) | function HasComplexFunction<T extends Node>(fn: (node: T) => boolean): (...
  function isSimpleTypeNamespaceTargetNoSelector (line 189) | function isSimpleTypeNamespaceTargetNoSelector(node: TypeNamespaceTarget...

FILE: src/format/core.ts
  function isNoopExpressionStatement (line 220) | function isNoopExpressionStatement(node: Node) {
  function getLastNotNoopExpressionStatement (line 224) | function getLastNotNoopExpressionStatement(parent: MaybeBlockBody) {
  function is_xVariableEqualishLike (line 228) | function is_xVariableEqualishLike(node: Node) {
  function is_BinaryishExpression (line 242) | function is_BinaryishExpression(node: Node): node is OrExpression | AndE...
  type StructSpread (line 254) | type StructSpread = StructLiteralPropertySpread | StructLiteralRestUnass...
  function is_StructSpread (line 255) | function is_StructSpread(node: Node): node is StructSpread {
  type ArrayLikeNode (line 266) | type ArrayLikeNode = Exclude<Extract<Node, { items: Node[] }>, UnionPatt...
  function isConciselyPrintedArray (line 267) | function isConciselyPrintedArray(node: ArrayLikeNode) {
  function printCommentsInsideEmptyArray (line 278) | function printCommentsInsideEmptyArray(path: AstPath<Node>) {
  function printNumber (line 287) | function printNumber(rawNumber: string) {
  function printOnOwnLine (line 297) | function printOnOwnLine(node: Node, printed: Doc) {
  function maybeEmptyLine (line 300) | function maybeEmptyLine(node: Node) {
  function printBodyOrCases (line 304) | function printBodyOrCases<T extends NodeWithBodyOrCases | BlockLikeMacro...
  function printMacroRules (line 345) | function printMacroRules<T extends MacroDeclaration | MacroRulesDeclarat...
  function is_unary_token (line 352) | function is_unary_token(item: MacroMatchSegment | undefined) {
  function can_unary (line 367) | function can_unary(node: MacroMatchSegment) {
  function is_optional_token (line 370) | function is_optional_token(item: MacroMatchSegment | undefined): item is...
  function is_optional_unary (line 373) | function is_optional_unary(item: MacroMatchSegment | undefined) {
  function is_optional_segment (line 377) | function is_optional_segment(item: Node): item is MacroGroup {
  function printRuleMatch (line 381) | function printRuleMatch<T extends MacroRuleDeclaration | MacroInlineRule...
  function printRuleTransform (line 637) | function printRuleTransform<T extends MacroRuleDeclaration | MacroInline...
  function is_AssignmentOrVariableDeclarator (line 684) | function is_AssignmentOrVariableDeclarator(node: Node): boolean {
  function hasLeadingOwnLineComment (line 687) | function hasLeadingOwnLineComment(node: Node): boolean {
  function isComplexDestructuring (line 697) | function isComplexDestructuring(node: Node): boolean {
  function isArrowFunctionVariableDeclarator (line 730) | function isArrowFunctionVariableDeclarator(node: Node) {
  function isObjectPropertyWithShortKey (line 733) | function isObjectPropertyWithShortKey(node: Node, keyDoc: Doc) {
  function print_CallExpression_end (line 740) | function print_CallExpression_end(print: print<CallExpression>, node: Ca...
  function printCallExpression (line 744) | function printCallExpression(print: print<CallExpression>, node: CallExp...
  function printTypeAnnotation (line 758) | function printTypeAnnotation<T extends Extract<Node, MaybeTypeAnnotation...
  function printAnnotatedPattern (line 761) | function printAnnotatedPattern<T extends Extract<Node, MaybeTypeAnnotati...
  function isLoneShortArgument (line 765) | function isLoneShortArgument(node: Node) {
  type Layout (line 783) | const enum Layout {
  function printMemberExpression (line 794) | function printMemberExpression(print: print<MemberExpression>, node: Mem...
  function shouldInlineMemberExpression (line 806) | function shouldInlineMemberExpression(node: MemberExpression, objectDoc:...
  function printAssignment (line 828) | function printAssignment(leftDoc: Doc, operator: string, rightPropertyNa...
  function is_MemberExpression_with_RangeOrLiteral_Property (line 980) | function is_MemberExpression_with_RangeOrLiteral_Property(node: Node | u...
  function is_Literal_or_SimpleRangeLiteral (line 985) | function is_Literal_or_SimpleRangeLiteral(node: Node) {
  function printMemberLookup (line 992) | function printMemberLookup(print: print<MemberExpression>, node: MemberE...
  function shouldPrint_CallExpression_chain (line 1000) | function shouldPrint_CallExpression_chain(node: CallExpression) {
  function is_MemberAccessLike (line 1003) | function is_MemberAccessLike(node: Node): node is ExpressionPath | Membe...
  type ChainItem (line 1012) | type ChainItem = { node: Node; printed: Doc; needsParens: boolean };
  function printMemberChain (line 1013) | function printMemberChain(print: print<CallExpression>, node: CallExpres...
  function isSimpleCallArgument (line 1240) | function isSimpleCallArgument(node: Node, depth: number) {
  function isLongCurriedCallExpression (line 1299) | function isLongCurriedCallExpression(node: Node) {
  function printTypeArguments (line 1310) | function printTypeArguments<T extends Extract<Node, MaybeGenericArgsTarg...
  function printLtParameters (line 1328) | function printLtParameters<T extends Extract<Node, ForLtParametersBody>>...
  function printGenerics (line 1346) | function printGenerics<T extends DeclarationNode>(print: print<T>, node:...
  function getPrintedTypeBounds (line 1364) | function getPrintedTypeBounds<T extends Extract<Node, TypeBoundsConstain...
  function printTypeBounds (line 1378) | function printTypeBounds<T extends Extract<Node, TypeBoundsConstaint>>(o...
  function printLtBounds (line 1390) | function printLtBounds<T extends Extract<Node, MaybeHasLtBounds>>(left: ...
  function printWhereBounds (line 1400) | function printWhereBounds<T extends DeclarationNode>(print: print<T>, no...
  function printDeclarationTypeBounds (line 1408) | function printDeclarationTypeBounds<T extends Extract<DeclarationNode, T...
  function printImplTraitForType (line 1415) | function printImplTraitForType(
  function adjustDeclarationClause (line 1424) | function adjustDeclarationClause(node: DeclarationNode, clause: ":" | " ...
  function hasNonWhereHeritageClause (line 1449) | function hasNonWhereHeritageClause(node: DeclarationNode) {
  function hasAnyHeritageClause (line 1483) | function hasAnyHeritageClause(node: DeclarationNode) {
  function hasMultipleHeritage (line 1487) | function hasMultipleHeritage(node: DeclarationNode) {
  function createGroupIdMapper (line 1495) | function createGroupIdMapper(description: string) {
  function printDanglingCommentsForInline (line 1500) | function printDanglingCommentsForInline(node: Node, marker?: DCM) {
  function isFormatLikeCall (line 1514) | function isFormatLikeCall(node: CallExpression | CallLikeMacroInvocation) {
  class ArgExpansionBailout (line 1524) | class ArgExpansionBailout extends Error {}
  function printCallArguments (line 1526) | function printCallArguments<T extends CallExpression | CallLikeMacroInvo...
  function shouldHugFunctionParameters (line 1620) | function shouldHugFunctionParameters(node: Extract<Node, FunctionLike>) {
  function shouldGroupFunctionParameters (line 1638) | function shouldGroupFunctionParameters(functionNode: FunctionDeclaration...
  function printBlockBody (line 1656) | function printBlockBody<T extends NodeWithBodyOrCases | BlockLikeMacroIn...
  function printMaybeBlockBody (line 1671) | function printMaybeBlockBody<T extends (NodeWithBodyOrCases | BlockLikeM...
  function printArrowFunction (line 1678) | function printArrowFunction<T extends ClosureFunctionExpression>(print: ...
  function printArrowChain (line 1734) | function printArrowChain(signatures: Doc[], shouldBreak: boolean, bodyDo...
  function printArrowFunctionSignature (line 1755) | function printArrowFunctionSignature<T extends ClosureFunctionExpression...
  function printGenerics_x_whereBounds (line 1776) | function printGenerics_x_whereBounds<T extends DeclarationNode>(print: p...
  function adjustClause (line 1788) | function adjustClause(
  function printParametersAndReturnType (line 1800) | function printParametersAndReturnType(node: FunctionNode | TypeFunctionN...
  function printFlowControlExpression (line 1808) | function printFlowControlExpression<T extends ReturnExpression | BreakEx...
  function flowControlExpressionNeedsOuterParens (line 1828) | function flowControlExpressionNeedsOuterParens(flow: ReturnExpression | ...
  function getLeftSide (line 1843) | function getLeftSide(node, includeAttributes = false) {
  function hasNakedLeftSide (line 1860) | function hasNakedLeftSide(node: Node) {
  function printReturnType (line 1871) | function printReturnType<T extends Extract<Node, MaybeReturnTypeConstrai...
  function printFunctionParameters (line 1879) | function printFunctionParameters<T extends FunctionNode | TypeFunctionNo...
  function path_try (line 1921) | function path_try<T>(callback: () => T): T {
  function shouldGroupFirstArg (line 1931) | function shouldGroupFirstArg(args: LocArray<ExpressionNode>): boolean {
  function shouldGroupLastArg (line 1942) | function shouldGroupLastArg(args: LocArray<ExpressionNode>): boolean {
  function couldGroupArg (line 1955) | function couldGroupArg(arg: Node, arrowChainRecursion = false): boolean {
  function isNonEmptyBlockStatement (line 1970) | function isNonEmptyBlockStatement(node: Node) {
  function isFunctionCompositionArgs (line 1974) | function isFunctionCompositionArgs(args) {
  function printBinaryishExpression (line 1993) | function printBinaryishExpression<T extends AndExpression | OrExpression...
  function shouldInlineLogicalExpression (line 2090) | function shouldInlineLogicalExpression(node: Node) {
  function printUnaryExpression (line 2098) | function printUnaryExpression<T extends UnaryExpression>(leftDoc: Doc, n...
  function printIfBlock (line 2109) | function printIfBlock<T extends IfBlockExpression>(print: print<T>, node...
  function printIfBlockCondition (line 2134) | function printIfBlockCondition<T extends IfBlockExpression | MatchExpres...
  function printCondition (line 2145) | function printCondition<T extends IfBlockExpression | MatchExpressionCas...
  function parenthesize_if_break (line 2158) | function parenthesize_if_break(doc: Doc) {
  function unwrapParenthesized (line 2161) | function unwrapParenthesized(doc: Doc) {
  function withParensIdentOnBreak (line 2168) | function withParensIdentOnBreak(node: Node, printed: Doc) {
  function isSimplePattern (line 2174) | function isSimplePattern(node: PatternNode | undefined) {
  function printUnionPattern (line 2202) | function printUnionPattern<T extends UnionPattern>(print: print<T>, node...
  function printArrayLike (line 2220) | function printArrayLike<T extends ArrayLikeNode>(print: print<T>, node: ...
  function printObject (line 2286) | function printObject<T extends ObjectNode>(print: print<T>, node: T): Doc {
  function printEnumBody (line 2340) | function printEnumBody<T extends EnumDeclaration>(print: print<T>, node:...

FILE: src/format/external.ts
  type CustomOptions (line 60) | interface CustomOptions extends ParserOptions<Node> {
  type NodeWithComments (line 72) | type NodeWithComments<T extends Node> = T & { comments: AnyComment[] };
  type MutatedComment (line 73) | interface MutatedComment extends Comment, PrettierCommentInfo {}
  type MutatedAttribute (line 74) | interface MutatedAttribute extends Attribute, PrettierCommentInfo {}
  type MutatedDocComment (line 75) | interface MutatedDocComment extends DocCommentAttribute, PrettierComment...
  type AnyComment (line 76) | type AnyComment = MutatedComment | MutatedAttribute | MutatedDocComment;
  type keyofDelimitedArrayProps (line 78) | type keyofDelimitedArrayProps<T> = T extends never ? never : keyof PickP...
  type DCM (line 82) | enum DCM {
  type PrettierCommentInfo (line 100) | interface PrettierCommentInfo {
  type AstPath (line 110) | interface AstPath<T = Node> {

FILE: src/format/plugin.ts
  function is_printing_macro (line 46) | function is_printing_macro() {
  function assertPathAtNode (line 50) | function assertPathAtNode(name: string, node: Node, ...ctx: any[]) {
  function f (line 55) | function f(...args: [strings: TemplateStringsArray, ...values: Doc[]]) {
  function sg_single (line 64) | function sg_single(s: TemplateStringsArray, v_0: Doc) {
  function sg_duo (line 67) | function sg_duo(s: TemplateStringsArray, v_0: Doc, v_1: Doc) {
  function pathCall (line 97) | function pathCall<T extends Node, K extends keyof NodeProps<T> & keyof T...
  function pathCallEach (line 101) | function pathCallEach<T extends Node, K extends AK<T>>(
  function pathCallAtParent (line 110) | function pathCallAtParent<T extends Node, R>(parent: T, fn: (parent: T) ...
  function pathCallParentOf (line 116) | function pathCallParentOf<T extends Node, R>(child: Node, fn: (parent: T...
  function pathCallTopMostIfBlockExpression (line 121) | function pathCallTopMostIfBlockExpression<R>(node: IfBlockExpression, fn...
  function print (line 126) | function print(property?: any, args?: any): Doc | Doc[] {
  function b (line 134) | function b(property: string, res = `${property} `): Doc {
  function map (line 137) | function map(property: string, mapItem?: MapFn<any, any>): Doc[] {
  function join (line 140) | function join(property: string, sep: SepFn<any, any> | Doc, trailingSep:...
  function map_join (line 143) | function map_join(
  type SepFn (line 162) | type SepFn<T extends Node = Node, K extends AK<T> = AK<T>> = <A extends ...
  type MapFn (line 163) | type MapFn<T extends Node = Node, K extends AK<T> = AK<T>> = <A extends ...
  type TrailingSepFn (line 164) | type TrailingSepFn<T extends Node = Node, K extends AK<T> = AK<T>> = <A ...
  type AV (line 165) | type AV<T extends Node, K extends keyof T> = Extract<NonNullable<T[K]>, ...
  type AK (line 166) | type AK<T extends Node> = keyof ArrayProps<T> & keyof T;
  type print (line 169) | interface print<T extends Node> {
  function genericPrint (line 181) | function genericPrint() {
  function canAttachComment (line 246) | function canAttachComment(n: Node) {
  method parse (line 270) | parse(code, parsers, options: CustomOptions) {
  method print (line 297) | print(path, options, print, args) {
  function devEndCheck (line 344) | function devEndCheck(printed: Doc) {

FILE: src/format/printer.ts
  type nPrint (line 65) | type nPrint<T extends Node> = (print: print<T>, node: T) => Doc | never;
  method [NodeType.MissingNode] (line 68) | [NodeType.MissingNode](print, node) {
  method [NodeType.SourceFile] (line 71) | [NodeType.SourceFile](print, node) {
  method [NodeType.Shebang] (line 78) | [NodeType.Shebang](print, node) {
  method [NodeType.Program] (line 81) | [NodeType.Program](print, node) {
  method [NodeType.Snippet] (line 84) | [NodeType.Snippet](print, node) {
  method [NodeType.Identifier] (line 87) | [NodeType.Identifier](print, node) {
  method [NodeType.Index] (line 90) | [NodeType.Index](print, node) {
  method [NodeType.LbIdentifier] (line 93) | [NodeType.LbIdentifier](print, node) {
  method [NodeType.McIdentifier] (line 96) | [NodeType.McIdentifier](print, node) {
  method [NodeType.LtIdentifier] (line 99) | [NodeType.LtIdentifier](print, node) {
  method [NodeType.PunctuationToken] (line 102) | [NodeType.PunctuationToken](print, node) {
  method [NodeType.DelimGroup] (line 105) | [NodeType.DelimGroup](print, node) {
  method [NodeType.Literal] (line 108) | [NodeType.Literal](print, node) {
  method [NodeType.ItemPath] (line 113) | [NodeType.ItemPath](print, node) {
  method [NodeType.ExpressionPath] (line 116) | [NodeType.ExpressionPath](print, node) {
  method [NodeType.TypePath] (line 119) | [NodeType.TypePath](print, node) {
  method [NodeType.Comment] (line 122) | [NodeType.Comment](print, node) {
  method [NodeType.DocCommentAttribute] (line 125) | [NodeType.DocCommentAttribute](print, node) {
  method [NodeType.Attribute] (line 128) | [NodeType.Attribute](print, node) {
  method [NodeType.MacroInvocation] (line 137) | [NodeType.MacroInvocation](print, node) {
  method [NodeType.MacroRulesDeclaration] (line 160) | [NodeType.MacroRulesDeclaration](print, node) {
  method [NodeType.MacroRuleDeclaration] (line 163) | [NodeType.MacroRuleDeclaration](print, node) {
  method [NodeType.MacroDeclaration] (line 166) | [NodeType.MacroDeclaration](print, node) {
  method [NodeType.MacroInlineRuleDeclaration] (line 169) | [NodeType.MacroInlineRuleDeclaration](print, node) {
  method [NodeType.MacroGroup] (line 172) | [NodeType.MacroGroup](print, node) {
  method [NodeType.MacroParameterDeclaration] (line 175) | [NodeType.MacroParameterDeclaration](print, node) {
  method [NodeType.PubSpecifier] (line 178) | [NodeType.PubSpecifier](print, node) {
  method [NodeType.ExternSpecifier] (line 195) | [NodeType.ExternSpecifier](print, node) {
  method [NodeType.ExpressionStatement] (line 198) | [NodeType.ExpressionStatement](print, node) {
  method [NodeType.UseStatement] (line 201) | [NodeType.UseStatement](print, node) {
  method [NodeType.DestructuredImport] (line 204) | [NodeType.DestructuredImport](print, node) {
  method [NodeType.AmbientImport] (line 218) | [NodeType.AmbientImport](print, node) {
  method [NodeType.AnonymousImport] (line 221) | [NodeType.AnonymousImport](print, node) {
  method [NodeType.NamedImport] (line 224) | [NodeType.NamedImport](print, node) {
  method [NodeType.ExternCrateStatement] (line 227) | [NodeType.ExternCrateStatement](print, node) {
  method [NodeType.TypeAliasDeclaration] (line 230) | [NodeType.TypeAliasDeclaration](print, node) {
  method [NodeType.LetVariableDeclaration] (line 242) | [NodeType.LetVariableDeclaration](print, node) {
  method [NodeType.ConstVariableDeclaration] (line 254) | [NodeType.ConstVariableDeclaration](print, node) {
  method [NodeType.StaticVariableDeclaration] (line 266) | [NodeType.StaticVariableDeclaration](print, node) {
  method [NodeType.ModuleDeclaration] (line 278) | [NodeType.ModuleDeclaration](print, node) {
  method [NodeType.ExternBlockDeclaration] (line 287) | [NodeType.ExternBlockDeclaration](print, node) {
  method [NodeType.FunctionDeclaration] (line 296) | [NodeType.FunctionDeclaration](print, node) {
  method [NodeType.FunctionSelfParameterDeclaration] (line 308) | [NodeType.FunctionSelfParameterDeclaration](print, node) {
  method [NodeType.FunctionParameterDeclaration] (line 311) | [NodeType.FunctionParameterDeclaration](print, node) {
  method [NodeType.FunctionSpread] (line 314) | [NodeType.FunctionSpread](print, node) {
  method [NodeType.StructDeclaration] (line 317) | [NodeType.StructDeclaration](print, node) {
  method [NodeType.StructPropertyDeclaration] (line 320) | [NodeType.StructPropertyDeclaration](print, node) {
  method [NodeType.TupleStructDeclaration] (line 323) | [NodeType.TupleStructDeclaration](print, node) {
  method [NodeType.TupleStructItemDeclaration] (line 326) | [NodeType.TupleStructItemDeclaration](print, node) {
  method [NodeType.UnionDeclaration] (line 329) | [NodeType.UnionDeclaration](print, node) {
  method [NodeType.EnumDeclaration] (line 332) | [NodeType.EnumDeclaration](print, node) {
  method [NodeType.EnumMemberDeclaration] (line 335) | [NodeType.EnumMemberDeclaration](print, node) {
  method [NodeType.EnumMemberTupleDeclaration] (line 345) | [NodeType.EnumMemberTupleDeclaration](print, node) {
  method [NodeType.EnumMemberStructDeclaration] (line 355) | [NodeType.EnumMemberStructDeclaration](print, node) {
  method [NodeType.TraitDeclaration] (line 365) | [NodeType.TraitDeclaration](print, node) {
  method [NodeType.AutoTraitDeclaration] (line 374) | [NodeType.AutoTraitDeclaration](print, node) {
  method [NodeType.TraitAliasDeclaration] (line 384) | [NodeType.TraitAliasDeclaration](print, node) {
  method [NodeType.ImplDeclaration] (line 393) | [NodeType.ImplDeclaration](print, node) {
  method [NodeType.NegativeImplDeclaration] (line 402) | [NodeType.NegativeImplDeclaration](print, node) {
  method [NodeType.ExpressionTypeSelector] (line 411) | [NodeType.ExpressionTypeSelector](print, node) {
  method [NodeType.ExpressionTypeCast] (line 414) | [NodeType.ExpressionTypeCast](print, node) {
  method [NodeType.ExpressionAsTypeCast] (line 417) | [NodeType.ExpressionAsTypeCast](print, node) {
  method [NodeType.ReturnExpression] (line 420) | [NodeType.ReturnExpression](print, node) {
  method [NodeType.BreakExpression] (line 423) | [NodeType.BreakExpression](print, node) {
  method [NodeType.ContinueExpression] (line 426) | [NodeType.ContinueExpression](print, node) {
  method [NodeType.YieldExpression] (line 429) | [NodeType.YieldExpression](print, node) {
  method [NodeType.RangeLiteral] (line 432) | [NodeType.RangeLiteral](print, node) {
  method [NodeType.CallExpression] (line 435) | [NodeType.CallExpression](print, node) {
  method [NodeType.MemberExpression] (line 438) | [NodeType.MemberExpression](print, node) {
  method [NodeType.AwaitExpression] (line 441) | [NodeType.AwaitExpression](print, node) {
  method [NodeType.UnwrapExpression] (line 444) | [NodeType.UnwrapExpression](print, node) {
  method [NodeType.ParenthesizedExpression] (line 447) | [NodeType.ParenthesizedExpression](print, node) {
  method [NodeType.MinusExpression] (line 453) | [NodeType.MinusExpression](print, node) {
  method [NodeType.NotExpression] (line 456) | [NodeType.NotExpression](print, node) {
  method [NodeType.OrExpression] (line 459) | [NodeType.OrExpression](print, node) {
  method [NodeType.AndExpression] (line 462) | [NodeType.AndExpression](print, node) {
  method [NodeType.ReassignmentExpression] (line 465) | [NodeType.ReassignmentExpression](print, node) {
  method [NodeType.UnassignedExpression] (line 468) | [NodeType.UnassignedExpression](print, node) {
  method [NodeType.OperationExpression] (line 471) | [NodeType.OperationExpression](print, node) {
  method [NodeType.ReassignmentOperationExpression] (line 474) | [NodeType.ReassignmentOperationExpression](print, node) {
  method [NodeType.ComparisonExpression] (line 477) | [NodeType.ComparisonExpression](print, node) {
  method [NodeType.LetScrutinee] (line 480) | [NodeType.LetScrutinee](print, node) {
  method [NodeType.ClosureFunctionExpression] (line 483) | [NodeType.ClosureFunctionExpression](print, node) {
  method [NodeType.ClosureFunctionParameterDeclaration] (line 486) | [NodeType.ClosureFunctionParameterDeclaration](print, node) {
  method [NodeType.BlockExpression] (line 489) | [NodeType.BlockExpression](print, node) {
  method [NodeType.LoopBlockExpression] (line 499) | [NodeType.LoopBlockExpression](print, node) {
  method [NodeType.WhileBlockExpression] (line 502) | [NodeType.WhileBlockExpression](print, node) {
  method [NodeType.ForInBlockExpression] (line 505) | [NodeType.ForInBlockExpression](print, node) {
  method [NodeType.IfBlockExpression] (line 508) | [NodeType.IfBlockExpression](print, node) {
  method [NodeType.TryBlockExpression] (line 511) | [NodeType.TryBlockExpression](print, node) {
  method [NodeType.MatchExpression] (line 514) | [NodeType.MatchExpression](print, node) {
  method [NodeType.MatchExpressionCase] (line 533) | [NodeType.MatchExpressionCase](print, node) {
  method [NodeType.StructLiteral] (line 551) | [NodeType.StructLiteral](print, node) {
  method [NodeType.StructLiteralPropertyShorthand] (line 554) | [NodeType.StructLiteralPropertyShorthand](print, node) {
  method [NodeType.StructLiteralProperty] (line 557) | [NodeType.StructLiteralProperty](print, node) {
  method [NodeType.StructLiteralPropertySpread] (line 560) | [NodeType.StructLiteralPropertySpread](print, node) {
  method [NodeType.StructLiteralRestUnassigned] (line 563) | [NodeType.StructLiteralRestUnassigned](print, node) {
  method [NodeType.ArrayLiteral] (line 566) | [NodeType.ArrayLiteral](print, node) {
  method [NodeType.SizedArrayLiteral] (line 569) | [NodeType.SizedArrayLiteral](print, node) {
  method [NodeType.TupleLiteral] (line 572) | [NodeType.TupleLiteral](print, node) {
  method [NodeType.ReferenceExpression] (line 575) | [NodeType.ReferenceExpression](print, node) {
  method [NodeType.RawReferenceExpression] (line 578) | [NodeType.RawReferenceExpression](print, node) {
  method [NodeType.DereferenceExpression] (line 581) | [NodeType.DereferenceExpression](print, node) {
  method [NodeType.BoxExpression] (line 584) | [NodeType.BoxExpression](print, node) {
  method [NodeType.UnionPattern] (line 587) | [NodeType.UnionPattern](print, node) {
  method [NodeType.ParenthesizedPattern] (line 590) | [NodeType.ParenthesizedPattern](print, node) {
  method [NodeType.RestPattern] (line 594) | [NodeType.RestPattern](print, node) {
  method [NodeType.WildcardPattern] (line 597) | [NodeType.WildcardPattern](print, node) {
  method [NodeType.PatternVariableDeclaration] (line 600) | [NodeType.PatternVariableDeclaration](print, node) {
  method [NodeType.StructPattern] (line 603) | [NodeType.StructPattern](print, node) {
  method [NodeType.StructPatternPropertyDestructured] (line 606) | [NodeType.StructPatternPropertyDestructured](print, node) {
  method [NodeType.StructPatternPropertyShorthand] (line 609) | [NodeType.StructPatternPropertyShorthand](print, node) {
  method [NodeType.TuplePattern] (line 612) | [NodeType.TuplePattern](print, node) {
  method [NodeType.ArrayPattern] (line 615) | [NodeType.ArrayPattern](print, node) {
  method [NodeType.ReferencePattern] (line 618) | [NodeType.ReferencePattern](print, node) {
  method [NodeType.BoxPattern] (line 621) | [NodeType.BoxPattern](print, node) {
  method [NodeType.MinusPattern] (line 624) | [NodeType.MinusPattern](print, node) {
  method [NodeType.RangePattern] (line 627) | [NodeType.RangePattern](print, node) {
  method [NodeType.TypeCall] (line 630) | [NodeType.TypeCall](print, node) {
  method [NodeType.TypeCallNamedArgument] (line 633) | [NodeType.TypeCallNamedArgument](print, node) {
  method [NodeType.TypeCallNamedBound] (line 636) | [NodeType.TypeCallNamedBound](print, node) {
  method [NodeType.LtElided] (line 639) | [NodeType.LtElided](print, node) {
  method [NodeType.LtStatic] (line 642) | [NodeType.LtStatic](print, node) {
  method [NodeType.TypeNever] (line 645) | [NodeType.TypeNever](print, node) {
  method [NodeType.TypeInferred] (line 648) | [NodeType.TypeInferred](print, node) {
  method [NodeType.GenericTypeParameterDeclaration] (line 651) | [NodeType.GenericTypeParameterDeclaration](print, node) {
  method [NodeType.ConstTypeParameterDeclaration] (line 658) | [NodeType.ConstTypeParameterDeclaration](print, node) {
  method [NodeType.GenericLtParameterDeclaration] (line 668) | [NodeType.GenericLtParameterDeclaration](print, node) {
  method [NodeType.WhereTypeBoundDeclaration] (line 671) | [NodeType.WhereTypeBoundDeclaration](print, node) {
  method [NodeType.WhereLtBoundDeclaration] (line 674) | [NodeType.WhereLtBoundDeclaration](print, node) {
  method [NodeType.TypeTraitBound] (line 677) | [NodeType.TypeTraitBound](print, node) {
  method [NodeType.TypeDynBounds] (line 680) | [NodeType.TypeDynBounds](print, node) {
  method [NodeType.TypeImplBounds] (line 683) | [NodeType.TypeImplBounds](print, node) {
  method [NodeType.TypeFnPointer] (line 686) | [NodeType.TypeFnPointer](print, node) {
  method [NodeType.TypeFnPointerParameter] (line 689) | [NodeType.TypeFnPointerParameter](print, node) {
  method [NodeType.TypeFunction] (line 692) | [NodeType.TypeFunction](print, node) {
  method [NodeType.TypeTuple] (line 695) | [NodeType.TypeTuple](print, node) {
  method [NodeType.TypeSizedArray] (line 698) | [NodeType.TypeSizedArray](print, node) {
  method [NodeType.TypeSlice] (line 702) | [NodeType.TypeSlice](print, node) {
  method [NodeType.TypeReference] (line 706) | [NodeType.TypeReference](print, node) {
  method [NodeType.TypeDereferenceConst] (line 709) | [NodeType.TypeDereferenceConst](print, node) {
  method [NodeType.TypeDereferenceMut] (line 712) | [NodeType.TypeDereferenceMut](print, node) {
  method [NodeType.TypeParenthesized] (line 715) | [NodeType.TypeParenthesized](print, node) {

FILE: src/format/styling.ts
  function needsOuterSoftbreakParens (line 114) | function needsOuterSoftbreakParens(node: Node) {
  function needsInnerParens (line 165) | function needsInnerParens(node: Node) {
  function precedenceNeedsParens (line 308) | function precedenceNeedsParens(node: LeftRightExpression | ClosureFuncti...
  function shouldFlatten (line 361) | function shouldFlatten(parent: ExpressionNode | ConditionExpression, nod...
  function needsParens (line 376) | function needsParens(node: Node) {
  function stmtNeedsSemi (line 380) | function stmtNeedsSemi(stmt: ExpressionStatement, disregardExprType = fa...
  function needsSemi (line 386) | function needsSemi(parent: NodeWithBody, stmt: ExpressionStatement, disr...
  function canInlineBlockBody (line 448) | function canInlineBlockBody(node: NodeWithBodyOrCases | BlockLikeMacroIn...
  function canInlineInlineable (line 495) | function canInlineInlineable(node: ExpressionWithBody) {
  type NodeWithBracketContent (line 545) | type NodeWithBracketContent =
  function emptyContent (line 557) | function emptyContent(node: NodeWithBracketContent): Doc {
  function is_insideScrutinee (line 598) | function is_insideScrutinee(target: Node) {
  function withPathAt (line 617) | function withPathAt<T extends Node, R>(target: T, callback: (target: T) ...
  function shouldPrintOuterAttributesAbove (line 626) | function shouldPrintOuterAttributesAbove(node: Node) {

FILE: src/transform/custom/attribute.ts
  type SimpleAttrItem (line 20) | type SimpleAttrItem =
  function transform_simpleAttrSyntax (line 28) | function transform_simpleAttrSyntax(segments: MacroInvocation["segments"...

FILE: src/transform/custom/cfg_if.ts
  function transform_macro_cfg_if (line 19) | function transform_macro_cfg_if(segments: MacroInvocation["segments"]) {

FILE: src/transform/custom/utils.ts
  function isIdent (line 4) | function isIdent(node: Segment | undefined, name?: string): node is Iden...
  function isToken (line 7) | function isToken(node: Segment | undefined, tk?: TK): node is Punctuatio...
  function isGroup (line 10) | function isGroup<D extends DelimKind>(node: Segment | undefined, dk?: D)...
  function isCallLike (line 14) | function isCallLike(tk_1: Segment | undefined, tk_2: Segment | undefined...

FILE: src/transform/index.ts
  type ExpressionLikeAttribute (line 90) | interface ExpressionLikeAttribute extends Attribute {
  type CallLikeMacroInvocation (line 94) | interface CallLikeMacroInvocation extends MacroInvocation {
  type BlockLikeMacroInvocation (line 102) | interface BlockLikeMacroInvocation extends MacroInvocation {
  function is_CallLikeMacroInvocation (line 108) | function is_CallLikeMacroInvocation(node: Node): node is CallLikeMacroIn...
  function is_BlockLikeMacroInvocation (line 112) | function is_BlockLikeMacroInvocation(node: Node): node is BlockLikeMacro...
  function is_CallExpression_or_CallLikeMacroInvocation (line 116) | function is_CallExpression_or_CallLikeMacroInvocation(node: any): node i...
  constant IGNORED_MACROS (line 120) | const IGNORED_MACROS = new Set([
  constant HARDCODED_MACRO_DELIMS (line 126) | const HARDCODED_MACRO_DELIMS = new Map<string, MacroInvocation["segments...
  function transform_ast (line 178) | function transform_ast(options: CustomOptions) {
  function maybe_transform_node (line 193) | function maybe_transform_node<T extends Node, S extends Snippet>(
  function isTransformed (line 209) | function isTransformed(node: Node) {
  method [NodeType.Attribute] (line 214) | [NodeType.Attribute](node) {
  method [NodeType.MacroInlineRuleDeclaration] (line 222) | [NodeType.MacroInlineRuleDeclaration](node) {
  method [NodeType.MacroInvocation] (line 226) | [NodeType.MacroInvocation](node) {
  method [NodeType.CallExpression] (line 282) | [NodeType.CallExpression](node) {
  method [NodeType.AutoTraitDeclaration] (line 294) | [NodeType.AutoTraitDeclaration](node) {
  method [NodeType.NegativeImplDeclaration] (line 297) | [NodeType.NegativeImplDeclaration](node) {
  method [NodeType.StructLiteral] (line 301) | [NodeType.StructLiteral](node) {
  method [NodeType.StructPattern] (line 304) | [NodeType.StructPattern](node) {
  function moveSpreadsToEnd (line 309) | function moveSpreadsToEnd(node: StructLiteral | StructPattern) {
  function mockBodyNoBody (line 324) | function mockBodyNoBody(node: NodeWithBodyNoBody) {
  function transformMacroDelim (line 329) | function transformMacroDelim(name: string, node: MacroInvocation): 1 | 2...
  function transformNode (line 347) | function transformNode<T extends Node>(node: T, parent?: Node, key?: str...
  function insert_blocks (line 367) | function insert_blocks(node: Node, parent?: Node, key?: string, index?: ...
  function flatten_typeBounds (line 403) | function flatten_typeBounds(topNode: Node) {
  function transform_nodeAttributes (line 454) | function transform_nodeAttributes(node: Node) {
  function registerPogramLike (line 494) | function registerPogramLike(program: Extract<Node, ProgramLike>) {
  function getCommentChildNodes (line 517) | function getCommentChildNodes(n: any): Node[] {

FILE: src/utils/common.ts
  type ErrorConstructor (line 5) | interface ErrorConstructor {
  type ImportMeta (line 10) | interface ImportMeta {
  function Narrow (line 15) | function Narrow<T extends R, R = unknown>(value: R): asserts value is T {}
  function AssertTypesEq (line 16) | function AssertTypesEq<A extends B, B>(...args: [B] extends [A] ? [] : [...
  type indexof (line 19) | type indexof<A> = A extends readonly any[] ? A extends 0 ? any : keyof A...
  type valueof (line 22) | type valueof<A> = A extends ReadonlyArray<infer U> ? A extends 0 ? any :...
  type vObject (line 25) | type vObject<V extends unknown = unknown, K extends unknown = unknown> =...
  type itfn (line 26) | type itfn<A, R> = (value: valueof<A>, key: indexof<A>) => R;
  type anySet (line 27) | type anySet<V extends unknown = unknown> = Set<V>;
  type anyMap (line 28) | type anyMap<K extends unknown = unknown, V extends unknown = unknown> = ...
  type anyfunction (line 29) | type anyfunction<A extends any[] = unknown[], R = unknown> = (...args: A...
  type objlike (line 30) | type objlike = object | anyfunction;
  type anymap (line 31) | type anymap<K extends unknown = unknown, V extends unknown = unknown> = ...
  function exit (line 33) | function exit(message: string, ...ctx: any[]): never {
  function assert (line 40) | function assert(predicate: boolean, err?: string, ...ctx: any[]): assert...
  function Identity (line 44) | function Identity<T>(v: T): T {
  function last_of (line 48) | function last_of<T extends ArrayLike<any>>(arr: T): T extends readonly [...
  function maybe_last_of (line 52) | function maybe_last_of<T extends readonly any[] | undefined>(
  function normPath (line 58) | function normPath(filepath: string) {
  function print_string (line 62) | function print_string(str: string) {
  function isArrayLike (line 72) | function isArrayLike(value: any): value is ArrayLike<unknown> {
  function oisArrayLike (line 76) | function oisArrayLike(value: {}): value is ArrayLike<unknown> {
  function binarySearchIn (line 80) | function binarySearchIn<T extends {}>(array: ArrayLike<T>, target: numbe...
  function getTerminalWidth (line 98) | function getTerminalWidth(fallbackWidth = 200) {
  function Map_get (line 128) | function Map_get<K, V>(map: anymap<K, V>, key: K, init: (key: K) => V): V {
  function isEmpty (line 132) | function isEmpty(array: ArrayLike<any>): boolean {
  function Array_splice (line 136) | function Array_splice<T extends any[]>(array: T, target: T[number], inde...
  function Array_replace (line 143) | function Array_replace<T extends any[]>(array: T, target: T[number], ......
  function has_key_defined (line 149) | function has_key_defined<T extends object, K extends T extends never ? n...
  function is_object (line 162) | function is_object(data: unknown): data is object | ({ [key: string]: un...
  function is_array (line 166) | function is_array(data: unknown): data is any[] {
  function ois_vobject (line 170) | function ois_vobject(data: any) {
  function each (line 184) | function each(data: any, callback: (value: any, index: any) => void): vo...
  function iLast (line 196) | function iLast(index: number, array: any[]) {
  function find_last (line 200) | function find_last<T>(arr: T[], test: itfn<T[], boolean>): T | undefined {
  function try_eval (line 204) | function try_eval<T>(fn: () => T): T | undefined {
  function clamp (line 212) | function clamp(min: number, max: number, value: number) {
  type MaybeFlatten (line 216) | type MaybeFlatten<T> = T extends ReadonlyArray<infer U> ? MaybeFlatten<E...
  type FlatArray (line 217) | type FlatArray<T> = MaybeFlatten<T>[];
  function flat (line 218) | function flat<T extends readonly any[]>(arr: T): FlatArray<T> {
  function flatMap (line 221) | function flatMap<T extends readonly any[], R>(arr: T, mapFn: (item: T[nu...
  function joinln (line 225) | function joinln(...arr: string[]): string {
  function join_lines (line 229) | function join_lines(fn: () => Generator<string, void, void>): string {
  function reduce_tagged_template (line 233) | function reduce_tagged_template<T>(args: [strings: TemplateStringsArray,...
  function map_tagged_template (line 238) | function map_tagged_template<T, R>(args: [strings: TemplateStringsArray,...
  function spliceAll (line 244) | function spliceAll<T extends any[]>(array: T): [...T] {
  function spread (line 250) | function spread<R>(fn: () => Iterable<R>): R[] {

FILE: src/utils/debug.ts
  function normPath_strip_cwd (line 5) | function normPath_strip_cwd(filepath: string) {
  type StackStyleFn (line 10) | type StackStyleFn = (callee: string, item: StackItem) => (str: string) =...
  type Stack (line 11) | interface Stack extends Array<StackItem> {
  class StackLine (line 16) | class StackLine {
    method constructor (line 24) | constructor(raw: string) {
  function getPrintWidth (line 40) | function getPrintWidth() {
  class StackItem (line 44) | class StackItem extends StackLine {
    method constructor (line 45) | constructor(private readonly stack: Stack, readonly i: number, raw: st...
    method hide (line 49) | hide() {
    method hideNext (line 53) | hideNext(n: number) {
    method hideWhileTrue (line 56) | hideWhileTrue(test: (line: StackItem) => boolean) {
    method at (line 60) | at(relIndex: number) {
    method next (line 63) | next() {
    method toString (line 66) | toString() {
  function createStack (line 75) | function createStack(message: string, Error_stack: string, style: Stack[...
  function composeStack (line 80) | function composeStack(stack: Stack) {
  function get_caller_cmd (line 87) | function get_caller_cmd(offset = 0) {
  function custom_prepareStackTrace (line 96) | function custom_prepareStackTrace(err, calls) {
  function overrideDefaultError (line 100) | function overrideDefaultError(silent = false) {
  function createCustomError (line 107) | function createCustomError({
  function compose2Cols (line 139) | function compose2Cols(left: string, right: string, len = 64, min = 1) {

FILE: tests/output-ext/expressions/block.f.rs
  function f (line 1) | fn f() {

FILE: tests/output-ext/expressions/closure.f.rs
  function main (line 1) | fn main() {
  function f (line 160) | fn f(_n: isize) -> isize {
  function f (line 163) | fn f() {
  type A (line 168) | struct A {
  type E (line 176) | enum E {
  type Ty (line 187) | type Ty = [

FILE: tests/output-ext/expressions/expr.f.rs
  function main (line 1) | fn main() {
  function f (line 253) | fn f() {
  function f (line 261) | fn f() {
  function public_expr (line 271) | pub fn public_expr(_: [u8; a(0).0]) {}
  function f (line 272) | pub fn f() {
  function f (line 275) | fn f() -> isize {
  function f (line 285) | fn f(x: Box<isize>) -> Box<(Box<isize>, Box<isize>)> {
  function f (line 288) | fn f<F>(f: F) -> isize where F: FnOnce(isize) -> isize {}
  function f (line 289) | fn f() {
  function f (line 297) | fn f() {
  function f (line 301) | fn f<T: ToString>(arg: T) -> String {
  function f (line 304) | fn f<A: Clone + 'static>(a: A, b: u16) -> Box<dyn Invokable<A> + 'static> {
  function f (line 307) | fn f() {
  type Foo (line 317) | pub trait Foo: Iterator<Item = <Self as Foo>::Key> {}
  function f (line 318) | fn f() {

FILE: tests/output-ext/expressions/flow_expr.f.rs
  function main (line 1) | pub fn main() {

FILE: tests/output-ext/expressions/ident.f.rs
  function bare_crate (line 1) | fn bare_crate(_: crate::a);
  function bare_global (line 2) | fn bare_global(_: ::a);
  function u8 (line 3) | fn u8(u8: u8) {
  function u8 (line 26) | pub fn u8<'u8: 'u8 + 'u8>(u8: &'u8 u8) -> &'u8 u8 {

FILE: tests/output-ext/expressions/literal.f.rs
  function f (line 155) | fn f() {

FILE: tests/output-ext/expressions/parens.f.rs
  function main (line 1) | fn main() {
  function cvgsk_nichqsd_bhvior (line 126) | fn cvgsk_nichqsd_bhvior() {
  method call_once (line 137) | extern "rust-call" fn call_once(mut self, arg: (&'a &'b [u16],)) -> (u8,...
  method call_once123 (line 140) | extern "rust-call" fn call_once123(
  method call_mut (line 146) | extern "rust-call" fn call_mut(
  function call_is_not_empty (line 154) | pub fn call_is_not_empty() {
  function x (line 165) | fn x() {

FILE: tests/output-ext/expressions/precedence.f.rs
  function main (line 1) | fn main() {
  type o (line 72) | type o = (
  function f (line 77) | pub extern "ABI" fn f() {
  function f (line 239) | fn f<T>() {

FILE: tests/output-ext/expressions/range.f.rs
  function q (line 1) | fn q() {
  function f (line 163) | fn f() {
  function f (line 166) | fn f() {
  function f (line 169) | fn f() {
  function foo (line 172) | fn foo(-128..=127: i8) {}

FILE: tests/output-ext/features/arbitrary_enum_discriminant.f.rs
  type Enum (line 3) | enum Enum {
  type Enum (line 9) | enum Enum {
  type E2 (line 18) | enum E2 {
  type E3 (line 23) | enum E3 {
  type E4 (line 28) | enum E4 {
  type ADT (line 32) | enum ADT {
  type CLike1 (line 36) | enum CLike1 {
  type CLike2 (line 42) | enum CLike2 {
  type CLike3 (line 49) | enum CLike3 {
  type ADT (line 55) | enum ADT {
  type NullablePointer (line 59) | enum NullablePointer {
  type Mixed (line 64) | enum Mixed {
  type MyWeirdOption (line 72) | enum MyWeirdOption<T> {
  type Test (line 76) | enum Test {
  type Foo (line 80) | pub enum Foo {
  type Bar (line 83) | pub enum Bar {
  type Size (line 88) | pub enum Size {
  type Signed (line 94) | enum Signed {
  type Unsigned (line 103) | enum Unsigned {

FILE: tests/output-ext/features/associated_type_bounds.f.rs
  type X (line 3) | type X = A<B: C>;
  function f (line 5) | fn f<F>(_: F) where F: for<'a> Trait<Output: 'a> {}
  function f (line 6) | fn f<'b, F>() where for<'a> F: Iterator<Item: 'a> + 'b {}
  type A (line 8) | trait A: MP {
    method f (line 9) | fn f<IM>(&self) -> i32 where for<'a> IM: T<T: U<<Self as MP>::T<'a>>>;

FILE: tests/output-ext/features/auto_traits.f.rs
  type T (line 3) | trait T {}
  type T (line 4) | unsafe auto trait T {}
  type T (line 5) | pub auto trait T {}
  type T (line 6) | pub unsafe auto trait T {}
  type T (line 8) | trait T {
  type T (line 13) | trait T {

FILE: tests/output-ext/features/const_generics_defaults.f.rs
  type Foo (line 3) | struct Foo<const N: usize = 1, const N2: usize = 2>;
  type Bar (line 4) | struct Bar<const N: usize, const N2: usize = { N + 1 }>;
  type Lots (line 5) | struct Lots<
  type Lott (line 9) | struct Lott<
  type NamesRHard (line 16) | struct NamesRHard<const N: usize = { 1 + 1 + 1 + 1 + 1 + 1 }>;
  type FooBar (line 17) | struct FooBar<
  type FooBarrrrrrrr (line 42) | struct FooBarrrrrrrr<

FILE: tests/output-ext/features/const_trait_impl.f.rs
  function foo (line 15) | fn foo() -> u8 where Self: ~const Bar {}
  type S (line 16) | struct S {
  type S (line 22) | struct S<
  type F (line 26) | trait F {
    method bar (line 27) | fn bar() where Self: ~const Foo;
    method c (line 28) | fn c<T: ~const Bar>();
  function qux (line 30) | const fn qux<T: ~const Foo>() {}
  function test1 (line 31) | const fn test1<T: ~const Foo + Bar>() {}
  function test2 (line 32) | const fn test2<T: ~const Foo + ~const Bar>() {}

FILE: tests/output-ext/features/destructuring_assignment.f.rs
  function main (line 3) | fn main() {

FILE: tests/output-ext/features/if_let_guard.f.rs
  function main (line 3) | fn main() {

FILE: tests/output-ext/features/inline_const.f.rs
  function f (line 3) | fn f() {

FILE: tests/output-ext/features/inline_const_pat.f.rs
  function f (line 3) | fn f() {

FILE: tests/output-ext/features/let_chains.f.rs
  function f (line 3) | fn f() {

FILE: tests/output-ext/macro/attributes.f.rs
  function a (line 4) | fn a() {}
  function b (line 6) | fn b() {
  function c (line 16) | fn c() {}
  function d (line 25) | fn d() {}
  function e (line 27) | fn e() {
  function f (line 45) | fn f() {}
  type A (line 70) | pub type A = Vec<B>;
    method f (line 416) | fn f(#[a1] self, #[a2] a: u8);
    method f (line 418) | fn f(#[a1] &self, #[a2] a: u8);
    method f (line 420) | fn f<'a>(#[a1] &'a mut self, #[a2] a: u8);
    method f (line 422) | fn f<'a>(#[a1] self: Box<Self>, #[a2] a: u8, #[a3] b: Vec<u8>);
    method f (line 424) | fn f(#[a1] #[a2] a: u8, #[a3] b: u8);
  function attr_3_3a_target (line 81) | fn attr_3_3a_target() {
  type foo_C (line 90) | trait foo_C {
  function main (line 96) | fn main() {
  constant C (line 300) | const C: C = C { #[attr] field: 0, #[attr] field: 1 };
  type S (line 302) | struct S;
  type I (line 304) | struct I {
  type I (line 309) | struct I(#[attr] u8, #[attr] pub u8);
  type BreaksWithComment (line 311) | struct BreaksWithComment(
  type C (line 316) | struct C {
  type Q (line 321) | struct Q {
  type A (line 324) | struct A<#[attr] 'a>();
    method f (line 416) | fn f(#[a1] self, #[a2] a: u8);
    method f (line 418) | fn f(#[a1] &self, #[a2] a: u8);
    method f (line 420) | fn f<'a>(#[a1] &'a mut self, #[a2] a: u8);
    method f (line 422) | fn f<'a>(#[a1] self: Box<Self>, #[a2] a: u8, #[a3] b: Vec<u8>);
    method f (line 424) | fn f(#[a1] #[a2] a: u8, #[a3] b: u8);
  type A (line 325) | struct A<#[attr] I>(I);
    method f (line 416) | fn f(#[a1] self, #[a2] a: u8);
    method f (line 418) | fn f(#[a1] &self, #[a2] a: u8);
    method f (line 420) | fn f<'a>(#[a1] &'a mut self, #[a2] a: u8);
    method f (line 422) | fn f<'a>(#[a1] self: Box<Self>, #[a2] a: u8, #[a3] b: Vec<u8>);
    method f (line 424) | fn f(#[a1] #[a2] a: u8, #[a3] b: u8);
  type E (line 326) | enum E {
  type E (line 329) | enum E<#[attr] 'b> {}
  type E (line 330) | enum E<#[attr] J> {}
  type T (line 331) | trait T {
  type T (line 334) | trait T<#[attr] 'c> {}
  type T (line 335) | trait T<#[attr] K> {}
  type Y (line 336) | type Y<#[attr] 'd> = ();
  type Y (line 337) | type Y<#[attr] L> = ();
  type A (line 339) | type A = fn(#[a1] u8, #[a2] ...);
    method f (line 416) | fn f(#[a1] self, #[a2] a: u8);
    method f (line 418) | fn f(#[a1] &self, #[a2] a: u8);
    method f (line 420) | fn f<'a>(#[a1] &'a mut self, #[a2] a: u8);
    method f (line 422) | fn f<'a>(#[a1] self: Box<Self>, #[a2] a: u8, #[a3] b: Vec<u8>);
    method f (line 424) | fn f(#[a1] #[a2] a: u8, #[a3] b: u8);
  function f (line 351) | fn f() {}
  function f (line 353) | fn f(#[a1] a: u8) {
  function f (line 356) | fn f<#[attr] 'g>() {}
  function f (line 357) | fn f<'e, #[attr] 'g>() {}
  function f (line 358) | fn f<#[attr] G>() {}
  function f (line 359) | fn f<E, #[attr] G>() {}
  function f (line 360) | fn f() where for<#[attr] 'i> X: for<#[attr] 'i> Y {}
  function f (line 361) | fn f(#[d(true)] a: i32, #[a2] b: i32, #[what = "how"] c: u32) {}
  function f (line 362) | fn f(#[a1] #[a2] a: i32, #[what = "how"] b: i32, #[e(true)] c: u32) {}
  function b (line 363) | fn b(#[cfg(x)] x: i32, y: i32) -> i32 {}
  function f (line 364) | fn f(
  function c (line 370) | fn c(#[cfg(foo)] &mut self, #[deny(C)] b: i32) {}
  function f (line 380) | pub fn f() {}
  function ffi (line 396) | fn ffi(#[a1] a: i32, #[a2] ...);
  function f (line 399) | unsafe extern "C" fn f(a: i32, #[a1] mut args: ...) {}
  method f (line 403) | fn f(#[a1] self, #[a2] a: u8) {}
  method f (line 405) | fn f(#[a1] &self, #[a2] a: u8) {}
  method f (line 407) | fn f<'a>(#[a1] &'a mut self, #[a2] a: u8) {}
  method f (line 409) | fn f<'a>(#[a1] self: Box<Self>, #[a2] a: u8) {}
  method f (line 411) | fn f(#[a1] #[a2] a: u8, #[a3] b: u8) {}
  type A (line 414) | trait A {
    method f (line 416) | fn f(#[a1] self, #[a2] a: u8);
    method f (line 418) | fn f(#[a1] &self, #[a2] a: u8);
    method f (line 420) | fn f<'a>(#[a1] &'a mut self, #[a2] a: u8);
    method f (line 422) | fn f<'a>(#[a1] self: Box<Self>, #[a2] a: u8, #[a3] b: Vec<u8>);
    method f (line 424) | fn f(#[a1] #[a2] a: u8, #[a3] b: u8);
  method foo (line 462) | fn foo(&mut self) -> isize {}
  method f2 (line 471) | pub fn f2(self) {
  method f3 (line 476) | fn f3(self) -> Q {}
  method f4 (line 485) | fn f4(self) -> A {}
  method f5 (line 489) | fn f5(self) -> X {}
  type Foo (line 492) | struct Foo {
  function f (line 511) | fn f() {}
  function f (line 513) | fn f() {
  function f (line 522) | fn f() {
  function f (line 531) | fn f() {}
  function f (line 538) | fn f() {
  type HP (line 596) | pub struct HP(pub u8);
  type A (line 599) | struct A {
    method f (line 416) | fn f(#[a1] self, #[a2] a: u8);
    method f (line 418) | fn f(#[a1] &self, #[a2] a: u8);
    method f (line 420) | fn f<'a>(#[a1] &'a mut self, #[a2] a: u8);
    method f (line 422) | fn f<'a>(#[a1] self: Box<Self>, #[a2] a: u8, #[a3] b: Vec<u8>);
    method f (line 424) | fn f(#[a1] #[a2] a: u8, #[a3] b: u8);
  function foo (line 606) | pub fn foo() {}
  function foo (line 611) | pub fn foo() {}
  type Params (line 616) | pub struct Params {
  type Os (line 659) | type Os = NoSource;
  function stmt_expr_attributes (line 661) | fn stmt_expr_attributes() {
  function a (line 667) | fn a() {
  function x (line 674) | fn x() {
  type Foo (line 687) | struct Foo {
  type Foo (line 694) | struct Foo;
  constant MIN_ALIGN (line 710) | const MIN_ALIGN: usize = 16;

FILE: tests/output-ext/macro/macro.invocation.f.rs
  function aux (line 20) | fn aux<Xs: HList, Ys: HList>(xs: Xs, ys: Ys) -> Expr!(Xs + Ys) where Xs:...
  function main (line 31) | fn main() {

FILE: tests/output-ext/macro/macro.tokens.f.rs
  function f (line 383) | fn f() {

FILE: tests/output-ext/macro/macro.transform.f.rs
  function $ident (line 3378) | fn $ident(arg: u32) -> u32;
  type S (line 3382) | struct S;
  type S (line 3389) | struct S;

FILE: tests/output-ext/miscellaneous/ast-program-locs-attr-dangling.f.rs
  type T (line 3) | struct T;

FILE: tests/output-ext/miscellaneous/ast-program-locs-attr.f.rs
  type T (line 4) | struct T;

FILE: tests/output-ext/miscellaneous/ast-program-locs.f.rs
  type T (line 3) | struct T;

FILE: tests/output-ext/patterns/pattern.f.rs
  function a (line 1) | fn a() {

FILE: tests/output-ext/patterns/rest.f.rs
  function b (line 1) | fn b() {

FILE: tests/output-ext/patterns/union.f.rs
  function fw1 (line 1) | fn fw1(H(Ok(mut x) | &Err(mut x)): H<R<'_>>) {}
  function f1 (line 2) | fn f1((Ok(mut x) | &Err(mut x)): R<'_>) {}
  function fw2 (line 3) | fn fw2(H(&(Ok(x) | Err(x))): H<R<'_>>) {}
  function fw3 (line 4) | fn fw3(H(Ok(x) | Err(x)): H<R<'_>>) {}
  function f2 (line 5) | fn f2(&(Ok(x) | Err(x)): R<'_>) {}
  function f3 (line 6) | fn f3((Ok(x) | Err(x)): R<'_>) {}
  function fun (line 7) | fn fun((A | B): _) {}
  function f (line 8) | fn f(x @ (A::R(_) | D::E(_)): Q) {}
  function x (line 10) | fn x() {

FILE: tests/output-ext/specifiers/extern.f.rs
  type funky_func (line 1) | type funky_func = extern "C" fn(
  function sup (line 11) | extern "C" fn sup() {}
  function some_fn (line 13) | fn some_fn() -> ();
  function quux (line 16) | fn quux() -> ();
  function syscall (line 17) | fn syscall(
  function foo (line 21) | unsafe fn foo() -> *mut Bar;
  function foo (line 22) | pub(super) const fn foo() -> *mut Bar;
  function foo (line 23) | pub(crate) unsafe fn foo() -> *mut Bar;

FILE: tests/output-ext/specifiers/pub.f.rs
  type E (line 1) | enum E {
  method f (line 9) | pub fn f() {}
  constant C (line 10) | pub const C: u8 = 0;
  type T (line 11) | pub type T = u8;
  method f (line 12) | pub(in foo) fn f(&self) -> i32 {
  type Pub (line 16) | pub struct Pub(Priv2);
  type Sized (line 20) | pub trait Sized {}
  constant MAIN (line 21) | const MAIN: u8 = {
  function f (line 47) | pub(super) fn f(_: Priv) {}
  function g (line 48) | pub(crate) fn g(_: Priv) {}
  function h (line 49) | crate fn h(_: Priv) {}
  type S1 (line 50) | pub(crate) struct S1;
  type S2 (line 51) | pub(super) struct S2;
  type S3 (line 52) | pub(self) struct S3;
  type S4 (line 53) | pub(in ::core) struct S4;
  type S5 (line 54) | pub(in a::b) struct S5;
  type Bar (line 58) | struct Bar(pub ());
  type C (line 59) | pub struct C(pub isize, isize);
  type D (line 60) | pub struct D(pub isize);
  type bool (line 61) | pub struct bool;
  type Pub (line 62) | pub struct Pub<T = Alias>(pub T);
  type S (line 67) | pub struct S {
  type Z (line 72) | pub(in Self::f) struct Z;

FILE: tests/output-ext/statements/const.f.rs
  constant X (line 1) | const X: u8;
  constant A (line 3) | const A: u8;
  constant A (line 4) | pub const A: Self::AssocTy = 1;
  constant FOO (line 5) | const FOO: dyn Fn() -> _ = "";
  constant FOO (line 6) | pub const FOO: &'static *const i32 = &(&0 as _);
  constant TEST (line 7) | const TEST: fn() -> _ = 1;
  constant MY_A (line 8) | const MY_A: A = A {

FILE: tests/output-ext/statements/enum.f.rs
  type E (line 1) | enum E {}
  type E (line 3) | enum E {
  type E (line 9) | enum E {
  type A (line 18) | enum A {
  type A (line 25) | enum A {
  type B (line 28) | enum B {
  type C (line 31) | enum C {
  type E (line 35) | enum E {
  type E (line 43) | enum E<T> {
  type E (line 47) | enum E<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized> {
  type E (line 94) | enum E<'a, 'b, 'c: 'b> {
  type X (line 100) | pub enum X<D> where D: Copy + Debug + Eq {}

FILE: tests/output-ext/statements/impl.f.rs
  method f (line 3) | fn f();
  method f (line 4) | fn f() {}
  method foo (line 9) | fn foo() {

FILE: tests/output-ext/statements/self.f.rs
  function f (line 1) | fn f(self) {}
  function f (line 2) | fn f(&self) {}
  function f (line 3) | fn f(mut self) {}
  function f (line 4) | fn f(&mut self) {}
  function f (line 5) | fn f(&'a self) {}
  function f (line 6) | fn f(&'a mut self) {}
  function f (line 7) | fn f(self: u8) {}
  function f (line 8) | fn f(mut self: u8) {}
  type X (line 9) | type X = fn(self);
  type X (line 10) | type X = fn(&self);
  type X (line 12) | type X = fn(&mut self);
  type X (line 13) | type X = fn(&'a self);
  type X (line 14) | type X = fn(&'a mut self);
  type X (line 15) | type X = fn(self: u8);
  function foo (line 17) | async fn foo<'b>(self: &'b Foo<'a>) -> &() {
  function f (line 20) | fn f<'b>(self: &'b Foo<'a>) -> &() {
  function f (line 23) | fn f<'a>(self: &Alias, arg: &'a ()) -> &() {
  function f (line 26) | fn f(&mut self) -> u32;
  function f (line 27) | fn f(mut self: Box<Self>);
  function f (line 28) | fn f(self: _) {}
  function f (line 29) | fn f(self: &_) {}
  function f (line 30) | fn f(&self) -> Self;

FILE: tests/output-ext/statements/spread.f.rs
  function main (line 1) | fn main() {}
  function f1_1 (line 2) | fn f1_1(x: isize, ...) {}
  function f1_2 (line 3) | fn f1_2(...) {}
  function f2_1 (line 4) | extern "C" fn f2_1(x: isize, ...) {}
  function f2_2 (line 5) | extern "C" fn f2_2(...) {}
  function f2_3 (line 6) | extern "C" fn f2_3(..., x: isize) {}
  function f3_1 (line 7) | extern fn f3_1(x: isize, ...) {}
  function f3_2 (line 8) | extern fn f3_2(...) {}
  function f3_3 (line 9) | extern fn f3_3(..., x: isize) {}
  function e_f1 (line 11) | fn e_f1(...);
  function e_f2 (line 12) | fn e_f2(..., x: isize);
  type X (line 14) | struct X;
    method i_f1 (line 16) | fn i_f1(x: isize, ...) {}
    method i_f2 (line 17) | fn i_f2(...) {}
    method i_f3 (line 18) | fn i_f3(..., x: isize, ...) {}
    method i_f4 (line 19) | fn i_f4(..., x: isize, ...) {}
  type T (line 21) | trait T {
    method t_f1 (line 22) | fn t_f1(x: isize, ...) {}
    method t_f2 (line 23) | fn t_f2(x: isize, ...);
    method t_f3 (line 24) | fn t_f3(...) {}
    method t_f4 (line 25) | fn t_f4(...);
    method t_f5 (line 26) | fn t_f5(..., x: isize) {}
    method t_f6 (line 27) | fn t_f6(..., x: isize);
  function foo (line 31) | pub fn foo(x: i32, ...);

FILE: tests/output-ext/statements/statements.f.rs
  constant _ (line 1) | const _: () = {
  function bar (line 14) | fn bar();
  function foo (line 16) | fn foo() {}
  type T (line 17) | type T = extern r#"C"# fn();
  function foo (line 18) | extern "\x43" fn foo() {}
  function bar (line 20) | fn bar();
  type T (line 22) | type T = extern "\x43" fn();
  function f1 (line 27) | fn f1();
  function f2 (line 28) | fn f2() {}
  function f3 (line 29) | fn f3();
  type X (line 31) | trait X {
    method f (line 32) | fn f();
    method f (line 33) | fn f() {}
    constant Y (line 34) | const Y: u8;
  function f (line 38) | fn f();
  function f (line 39) | fn f();
  type A (line 46) | type A = u8;
  type A (line 47) | type A<'a: 'static, T: Ord + 'static>
  function test (line 51) | const async fn test() {}
  function test (line 52) | async unsafe fn test() {}
  function test (line 53) | const unsafe fn test() {}
  function test (line 54) | unsafe extern fn test() {}
  function f (line 55) | fn f() {
  function f (line 62) | const fn f(a: *const i32, b: i32) -> bool {}
  function f (line 63) | unsafe fn f(&self) -> u32;
  function f (line 64) | const unsafe fn f(v: u32) -> u32 {}
  function f (line 65) | unsafe fn f(func: unsafe fn() -> ()) -> () {}
  type Range (line 66) | struct Range<
  function f (line 72) | fn f(d: [u8; 1 + 1]) -> A<T, [u8; 1 + 1]>
  function f (line 74) | fn f<'a, 'b, 'c, T>(x: foo::X<'a, T, 'b, 'c>) {}
  function f (line 75) | fn f() -> Option<fn() -> Option<bool>> {
  function a (line 78) | fn a() {
  type Test3 (line 86) | enum Test3 {
  type E (line 93) | enum E {
  function foobar (line 100) | fn foobar<F, G>() -> usize where (): Foobar<F, G> {}
  function free (line 104) | pub fn free(x: *const u8);
  type C (line 113) | trait C<A> {
    method D (line 114) | fn D<B, F>(&self, f: F) where F: FnMut(A) -> Q<B>;
  type A (line 116) | trait A<T: B = Self> {}
  type A (line 117) | trait A: B::C {}
  type A (line 118) | trait A<T>: B::C {}
  function f (line 119) | fn f() -> () {}
  function f (line 120) | fn f<T: X<Y<()> = i32>>() {}
  function f (line 121) | fn f<F>(mut f: F) where F: FnMut(&mut R, bool) {}
  function f (line 122) | fn f<F>(f: F) where F: for<'a> Fn(&'a isize, &'a isize) -> isize {}
  function f (line 123) | fn f<F>(f: F) -> isize where F: Fn() -> isize {
  function g (line 126) | async fn g(((ref a, ref mut b), (ref mut c, ref d)): ((A, A), (A, A))) {}
  function bar (line 127) | pub unsafe extern "C" fn bar(_: i32, mut ap: ...) -> usize {}
  function f (line 128) | unsafe fn f(&self, x: &usize) {
  function f (line 131) | fn f<B: ?Sized + Q>() {}
  function f (line 132) | fn f<A, F: for<'a> F<(&'a A,)>>(_: F) {}
  function f (line 133) | fn f<M>(a: M) where M: A, M::B: C {}
  function f (line 134) | fn f<T, A>(t: fn(&A)) where fn(&A): for<'a> F<(&'a A,)> {}
  function rust_no_mangle (line 136) | pub extern "C" fn rust_no_mangle() -> i32 {}
  function foo (line 137) | pub fn foo<'a, 'b>(x: Foo<'a, 'b>, _o: Option<&&()>) {
  constant x (line 140) | const x: &'static dyn Fn() = &(|| e!("q"));
  function foo (line 141) | const fn foo() -> i32 {}
  function foo (line 142) | extern "\x43" fn foo() {}
  function bar (line 145) | fn bar();
  type T (line 148) | type T = extern "\x43" fn();
  function foo (line 149) | fn foo() {}
  function bar (line 152) | fn bar();
  type T (line 155) | type T = extern r#"C"# fn();

FILE: tests/output-ext/statements/struct.f.rs
  type S (line 1) | struct S<A: ?for<'a> Q, B: 'a + Q, C: 'a, G: Q + 'a, H: Q, I:>;
  type Empty1 (line 2) | struct Empty1 {}
  type Empty2 (line 3) | struct Empty2;
  type Empty7 (line 4) | struct Empty7();
  type Align8Many (line 5) | struct Align8Many {
  type A (line 11) | struct A<T>([T]);
  type A (line 12) | struct A<T>(T);
  type cat (line 13) | struct cat {
  type Test3 (line 17) | struct Test3<'a, 'b, 'c> {
  type Test4 (line 26) | struct Test4<'a, 'b: 'a> {
  type Test6 (line 29) | struct Test6<'a, 'b: 'a> {
  type Foo (line 32) | struct Foo<'a> {
  type X (line 35) | struct X;
  type U (line 36) | struct U {}
  type P (line 37) | struct P<T>(T);
  type A (line 38) | struct A<U> where U: E(U);
  type A (line 39) | struct A<U> where U: E(U) -> R;
  type A (line 40) | struct A<U>(U) where U: Eq;
  type K (line 41) | struct K<'a>(&'a ());
  type A (line 42) | pub struct A([u8; 1]);
  type S (line 43) | pub(crate) struct S<'a, I, E>(I, &'a E);
  type A (line 44) | pub struct A<T: B>(C);
  type A (line 45) | pub struct A<T = u8>(T);
  type Table (line 46) | pub struct Table<T, const N: usize>([Option<T>; N]);
  type B (line 47) | struct B;
  type A (line 48) | struct A<T: ?Sized>(C<T>, ());
  type A (line 49) | struct A<T = i32, U = i32>(B<T, U>) where B<T, U>: Marker;
  type A (line 50) | struct A<T = u32, U = i32>(T, U) where B<T, U>: Marker;
  type A (line 51) | struct A<'a, S: B<'a> = i32>(S, &'a ());
  type S1 (line 52) | struct S1(pub(in foo) (), pub T, pub(crate) (), pub ((), T));
  type G (line 53) | struct G<T, U>(*const T, *const U);
  type Unique (line 54) | pub struct Unique<T: ?Sized> {
  type A (line 58) | pub struct A(u32, ::b::Q);
  type S (line 59) | struct S(<AT as A<DT>>::AS);
  type A (line 60) | pub struct A<I> where I: B<C: D> {

FILE: tests/output-ext/statements/trait.f.rs
  type A (line 1) | trait A {}
  type A (line 2) | trait A<B: C> {}
  type T2 (line 3) | trait T2<'x, 'y>: T1<'x> {}
  type A (line 4) | trait T<E, R> = S<A> where <Self as B<D>>::T: H<R>;
  type A (line 6) | trait A<S: B<C = S>>: D<S> {}
  type A (line 7) | trait A = B<C = D>;
  type A (line 10) | trait A = std::fmt::Display + std::fmt::Debug;
  type A (line 20) | trait A<X: ?Sized> {}
  type A (line 21) | trait A<X: ?Sized, Y> {}
  type A (line 22) | trait A<Y, X: ?Sized> {}
  type A (line 23) | trait A<X: ?Sized, Y: ?Sized> {}
  type A (line 24) | trait A<X: ?Sized + T2> {}
  type A (line 25) | trait A<X: T2 + ?Sized> {}
  type A (line 27) | pub trait A {}
  type A (line 29) | pub trait A<R: D>: Sized {}

FILE: tests/output-ext/statements/union.f.rs
  type union (line 6) | struct union;
  function new (line 9) | pub fn new() -> Self {
  function main (line 14) | fn main() {

FILE: tests/output-ext/statements/use.f.rs
  type Foo (line 34) | crate struct Foo;
  function test (line 98) | fn test() {

FILE: tests/output-ext/types/cast.f.rs
  function a (line 1) | fn a() {
  constant A (line 36) | const A: *const u8 = &0 as *const _ as *const Q as *const u8;

FILE: tests/output-ext/types/never.f.rs
  function a (line 1) | fn a() {
  function a (line 7) | fn a(x: !) -> ! {
  function foo (line 10) | fn foo(never: !) {}
  function a (line 11) | fn a(x: !) {}
  function a (line 12) | fn a(ref x: !) {}
  function a (line 13) | fn a(x: &[!]) {}
  function a (line 14) | fn a(x: B<(), !>) {}
  method c (line 17) | fn c(&self, d: &!) -> E {}
  type A (line 20) | type A = !;

FILE: tests/output-ext/types/types.f.rs
  type A (line 1) | type A where 'a: 'b + 'c = u8;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 2) | type A where 'a: 'b + 'c = u8;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 3) | type A where 'a: 'b = u8;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 4) | type A where 'a: 'b = u8;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 7) | type A = u8;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 11) | type A = for<'a: 'b> fn();
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 12) | type A = for<'a: 'b> fn();
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 13) | type A = for<'a: > fn();
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 14) | type A = for<'a: > fn();
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 15) | type A = for<'a> fn();
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 16) | type A = for<> fn();
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 18) | type A = Box<dyn (Fn(u8) -> u8) + 'static + Send + Sync>;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 19) | type A = impl B;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 21) | type A = u8;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 22) | type A where for<'a> dyn for<'b> Trait1 + ?Trait2: 'a + Trait = u8;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 24) | type A where T: Trait = u8;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 25) | type A where T: Trait + Trait = u8;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 26) | type A where T: Trait = u8;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 29) | type A = <m::Alias as m::Trait>::X;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 31) | pub type A<T> where T: B = T;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type B (line 34) | type B: Ord = u8;
    constant B (line 386) | const B: u8 = 0;
  type E (line 37) | type E<_T>: Ord = u8;
  type Y (line 40) | type Y<T> where Self: Sized = u32;
  constant FN (line 43) | pub const FN: &'static fn() = &(fop::<i32> as fn());
  constant A (line 44) | const A: &&&u32 = &&&42;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  constant CONST1 (line 45) | const CONST1: &[bool; 1] = &[true];
  constant CONST (line 46) | const CONST: &[Option<()>; 1] = &[Some(())];
  constant A (line 47) | const A: [u32; 1] = [4];
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  constant F (line 48) | const F: &'static dyn PartialEq<u32> = &7u32;
  type R (line 49) | struct R<'a> {
  function g (line 52) | fn g() -> impl Tr2<m::Alias> {}
  function leak_dyn_nonprincipal (line 53) | fn leak_dyn_nonprincipal() -> Box<dyn PubPrincipal + PrivNonPrincipal> {}
  function method (line 54) | fn method() -> Self::Pub {}
  function f (line 55) | fn f<T: PrivTr>(arg: T) {}
  function unused (line 56) | pub fn unused<const T: usize>() -> usize {}
  function start (line 57) | fn start(_: isize, _: *const *const u8) -> isize {}
  function as_ptr (line 58) | fn as_ptr(&self) -> *const Self::Item;
  function as_mut_ptr (line 59) | fn as_mut_ptr(&mut self) -> *mut Self::Item;
  function as_ptr (line 60) | fn as_ptr(&self) -> *const T {
  function as_mut_ptr (line 63) | fn as_mut_ptr(&mut self) -> *mut T {
  function y_uses_f (line 66) | fn y_uses_f(f: impl Fn()) {}
  function infer (line 67) | fn infer<T: a::B>(c: T) -> T {
  function f1 (line 70) | fn f1<'a, 'b, 'c>(_x: &'a u32, _y: &'b u32, _z: &'c u32) where 'c: 'a + ...
  function syntax (line 71) | fn syntax() {
  function A (line 148) | fn A(x: Option<fn(i32)>) -> Option<unsafe fn(i32)> {}
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  function f (line 149) | fn f(x: fn(i32)) -> unsafe fn(i32) {}
  function f (line 150) | fn f<'b, L: X<&'b Q<K>>>() {}
  type A (line 151) | struct A<T, U = [u8; a::<T>()]>(T, U);
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type Q (line 153) | trait Q<P = Self> {}
  type Q (line 154) | trait Q<P: Sized = [Self]> {}
  type H (line 155) | trait H<'d, 'e>: for<'f> I<'d, 'f, 'e> + 'd {}
  type F (line 156) | trait F<'f>: for<'a> A<'a> + for<'e> E<'e> {}
  type Q (line 157) | struct Q<A = S, T>(A, T);
  type Q (line 158) | struct Q<A, B = Vec<C>, C>(A, B, C);
  function f (line 160) | fn f<T: B<'a>>(self) -> &'a str {}
  function A (line 162) | extern "C" fn A<T: Add>(a: T, b: T) -> T::Output {
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  function f (line 166) | pub fn f<'a>(x: &'a i32);
  function f (line 167) | pub fn f<'b>(x: &'a i32, y: &'b i32);
  function f (line 168) | pub fn f<'a>(x: &'a i32, y: &i32) -> &'a i32;
  function f (line 169) | pub fn f<'b>(x: for<'c> fn(&'a i32));
  function f (line 170) | pub fn f<'b>(x: for<'c> fn(&'b i32));
  function f (line 171) | pub fn f<'b>(x: for<'c> fn(&'c i32));
  function f (line 172) | pub fn f<'b>() -> for<'c> fn(&'a i32);
  function f (line 173) | pub fn f<'b>() -> for<'c> fn(&'b i32);
  function f (line 174) | pub fn f<'b>() -> for<'c> fn(&'c i32);
  type X (line 176) | struct X<'x, 'y> {
  type G (line 180) | struct G<T> where for<'f> T: F<'f, As: E<'f>> + 'f {
  type D (line 183) | struct D<T> where T: for<'c> C<'c, As: A<'c>> {
  function f (line 186) | fn f<T>() where T: A, T::U: B {}
  function f (line 187) | fn f(a: isize, b: *const *const u8) -> isize {}
  function f (line 188) | fn f<G: FnMut(A, A) -> A>(mut a: G, b: A, c: A) -> A {}
  function f (line 189) | fn f<T: A<B = T> + C>(x: T) -> T {}
  function f (line 190) | fn f<A, B: a<A>>(x: B) -> C<A> {}
  type Whitespace (line 191) | struct Whitespace<T: Clone = ()> {
  type TokenSplit (line 194) | struct TokenSplit<T: Clone = ()> {
  function f (line 197) | fn f<'a, 'b, T>(t: T) -> isize where T: 'a, 'a: 'b, T: Eq {
  function f (line 201) | fn f() where T: for<'a> A<'a> + 'a {}
  function f (line 202) | fn f() where T: for<'g> H<'g, 'g, As: for<'h> H<'h, 'g> + 'g> {}
  function f (line 203) | fn f()
  function f (line 210) | fn f()
  function f (line 217) | fn f()
  function f (line 224) | fn f() where T: Fn(&(), &()) {}
  function f (line 225) | fn f() where T: Fn(&'a (), &()) {}
  function f (line 226) | fn f() where T: Fn(&(), Box<dyn Fn(&())>) {}
  function f (line 227) | fn f() where T: Fn(&(), fn(&())) {}
  function f (line 228) | fn f() where T: Fn(&(), for<'a> fn(&'a ())) {}
  function f (line 229) | fn f() where T: Fn(&(), Box<dyn Fn(&())>, &(), fn(&(), &())) {}
  function f (line 230) | fn f() where T: for<'a> Fn(&'a (), &()) {}
  function f (line 231) | fn f() where T: for<'a> Fn(&(), &'a ()) {}
  function f (line 232) | fn f() where T: for<'a> Fn(&'a (), &'a ()) {}
  function f (line 233) | fn f() where T: for<'a> Fn(&'a (), Box<dyn Fn(&())>) {}
  function f (line 234) | fn f() where T: for<'a> Fn(&(), Box<dyn Fn(&())>, &'a (), fn(&(), &())) {}
  function f (line 235) | fn f() where T: A, F: FnOnce(B<T>) -> bool {}
  function f (line 236) | fn f() -> impl std::borrow::Borrow<<u8 as A>::S> {}
  function f (line 237) | fn f(_: <() as A<Self::B>>::C) {}
  type S (line 238) | struct S<>;
  type T (line 239) | trait T<> {}
  type E (line 240) | enum E<> {
  function f (line 244) | fn f<'a>(x: for<'b, 'c: 'a + 'b> fn(&'a A, &'b B) -> &'c C)
  type S (line 248) | struct S<F: for<'a, 'b: 'a> Fn(&'a A, &'b B) -> &'c C>(F);
  type S (line 249) | struct S<F>(F) where F: for<'a, 'b: 'a> Fn(&'a A, &'b B) -> &'c C;
  type S (line 250) | struct S(dyn for<'a, 'b: 'a> Fn(&'a A, &'b B) -> &'c C);
  type T (line 251) | type T = Box<dyn for<'a, 'b: 'a> Fn(&'a A, &'b B) -> &'c C>;
  type L8 (line 252) | type L8<T> = L<L<L<L<L<L<L<L<T>>>>>>>>;
  type L64 (line 253) | type L64<T> = L8<L8<L8<L8<T>>>>;
  type Y (line 258) | type Y<'a> = &'a ();
  type Z (line 274) | type Z = dyn for<'x> Send;
  type A (line 275) | type A = (*const E::R, D);
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  function f (line 276) | fn f(&self) -> Pin<Box<dyn Future<Output = Self::B> + '_>>;
  function f (line 277) | fn f(&self) -> Self::Y<'_> {}
  function f (line 278) | fn f(x: &()) -> &() {}
  function f (line 279) | fn f(x: &impl for<'a> X<Y<'a> = &'a ()>) -> &() {}
  function f (line 280) | fn f<'a, T: for<'b> Fun<F<'b> = T>>(t: T) -> T::F<'a> {}
  function f (line 281) | fn f<'a, T: Fun<F<'a> = T>>(t: T) -> T::F<'a> {}
  function f (line 282) | fn f<T: ?for<'a> Sized>() {}
  function f (line 283) | fn f<'a, T1: X<Y = T1>>(t: T1) -> T1::Y<'a>;
  function f (line 284) | fn f<'a>(s: Box<dyn X<Y<'a> = &'a ()>>) {}
  function f (line 285) | fn f<'a>(t: Self::Y<'a>) -> Self::Y<'a>;
  function f (line 286) | fn f<T: for<'a> X<Y<'a> = &'a ()>>(x: &T) -> &() {}
  function f (line 287) | fn f<'a, T: ?Sized + Fun<F<'a> = [u8]>>(_: Box<T>) -> &'static T::F<'a> {}
  function f (line 288) | fn f<'a>(t: &'a Self::F<'a>) -> &'a Self::F<'a> {}
  function f (line 289) | fn f<T>() where T: S, for<'a> T::Item<'a>: Q {}
  function f (line 290) | fn f<'c, 'd>(s: Box<dyn X<Y = (&'c u32, &'d u32)>>) {}
  function f (line 291) | fn f(e: &impl for<'a> X<Y<'a> = &'a ()>) -> &'static () {}
  function f (line 292) | fn f<T: for<'a> X<Y<'a> = &'a ()>>(x: &T) -> &'static () {}
  function f (line 293) | fn f(x: &mut dyn for<'a> E<R<'a> = &'a i32>) -> usize {}
  function f (line 294) | fn f() where 'static: 'static, dyn 'static: 'static + Copy {}
  function f (line 295) | fn f() where 'static: 'static, dyn 'static + ::Foo: 'static + Copy {}
  function f (line 296) | fn f<F: A>() where F::B: Copy {}
  function f (line 297) | fn f<F: A>() where <F as A>::B: Copy {}
  function f (line 298) | fn f<F: A<B: A>>() where F::B: Copy {}
  function f (line 299) | fn f<T: S<<Self as A>::Q>>(&self, r: &T) -> u64;
  function f (line 300) | fn f() -> impl Default {}
  function f (line 301) | fn f(t: Box<dyn for<'a> Get<i32, i32>>) {}
  function f (line 302) | fn f(t: Box<dyn for<'a> Fn(i32) -> i32>) {}
  function f (line 303) | fn f(t: for<'a> fn(i32) -> i32) {}
  function f (line 304) | fn f(t: for<'a> unsafe fn(i32) -> i32) {}
  function f (line 305) | fn f(t: for<'a> extern "C" fn(i32) -> i32) {}
  function f (line 306) | fn f(t: for<'a> unsafe extern "C" fn(i32) -> i32) {}
  function f (line 309) | async fn f(_: impl for<'a> Add<&'a u8>, _: impl for<'b> Add<&'b u8>) {}
  function f (line 310) | async fn f<'a>(_: &'a ()) -> impl A<dyn B> {}
  function f (line 311) | fn f<D: A>() where D::S: {}
  type R (line 313) | struct R<'a> {
  function f (line 316) | fn f() -> [u8; 4 * 1024 * 1024 * 1024 * 1024] {}
  type Foo (line 317) | trait Foo where T: Borrow<U> + ?Sized, U: ?Sized + 'b, 'a: 'b, Box<T>: {}
  type Map (line 318) | trait Map
  type S (line 321) | trait S: A + AsRef<Self::B> {}
  type Bar (line 322) | struct Bar<const N: u8>([u8; (N + 2) as usize]) where [(); (N + 2) as us...
  function f (line 323) | fn f<const N: u8>() where D<{ N as usize as u16 }>: {}
  function f (line 324) | fn f<T>()
  function f (line 330) | fn f<'u, 'a, F>()
  function f (line 332) | fn f(&self, db: &<Q as QueryDb<'_>>::DynDb) {}
  function f (line 333) | pub fn f<'a, I>() -> impl B<I, D = (), C = impl S + 'a>
  type S (line 335) | type S<T: A<B: for<'a> C<&'a u8>>> = D;
  type A (line 337) | type A = a::b!();
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  function f (line 339) | fn f(&self, a: &!) {}
  type S (line 341) | type S<'a, T: Debug + 'a>: ?Sized = dyn Iterator<Item = T>;
  type S (line 342) | type S<'x> where T: 'x = &'x ();
  type S (line 343) | type S<'u, 'v> where 'u: 'v = &'v &'u ();
  type S (line 344) | type S where Self: Q + S = E;
  type S (line 345) | type S<'a: 'b, 'b> = (&'a (), &'b ());
  type S (line 346) | type S<'a> where Self: 'static = &'a ();
  type S (line 347) | type S<'a, 'b> where 'b: 'a = (&'a (), &'b ());
  type S (line 351) | type S<'a> = &'a ();
  type S (line 354) | type S<'a> where <A as B>::T: 'a, <A as B>::T: 'a = R<&'a S::T, &'a E::T>;
  type S (line 355) | type S<T> = Self::E<'static, T>;
  type S (line 356) | type S = Self::E<'static, 'static>;
  type S (line 361) | type S = Q<<T as R>::E>;
  type B (line 362) | struct B<'a, T: for<'r> X<Y<'r> = &'r ()>> {
    constant B (line 386) | const B: u8 = 0;
  type E (line 365) | enum E<'a> {
  type T (line 368) | pub type T<P: Send + Send + Send> = P;
  type S (line 369) | type S<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>);
  type S (line 370) | type S<'b, T: 'b + 'b> = (&'b u32, Vec<T>);
  type S (line 371) | type S<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>);
  type A (line 372) | type A = dyn S + ?Sized + ?Sized;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type R (line 373) | type R = dyn ?Sized + A;
  type Q (line 374) | type Q = dyn for<'a> E<'a> + for<'b> R<'b>;
  type S (line 375) | type S = dyn Q<for<'a> fn(&'a u8)> + G<for<'b> fn(&'b u8)>;
  type A (line 376) | type A = dyn ?Sized;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 377) | type A = <S as Tr>::A::f<u8>;
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type A (line 378) | trait A: B<i32> + std::fmt::Debug + Send + Sync {}
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type R (line 379) | struct R<Z: ?Sized = E<i32, i32>>(Z);
  type A (line 381) | trait A {
    type C (line 255) | type C<'a> = <T as D>::E<'a, 'static>;
    constant A (line 382) | const A: u8 = 0;
  type B (line 385) | pub trait B {
    constant B (line 386) | const B: u8 = 0;
  type C (line 389) | pub trait C: A + B {
    constant C (line 390) | const C: u8 = 0;
  type b (line 398) | pub type b = Box<dyn t + sync::Send + sync::Sync + 'static>;
  type b (line 399) | pub type b = Box<dyn for<'tcx> e<'tcx> + sync::Send + sync::Sync + 'stat...

FILE: tests/output/comments/assignment.f.rs
  constant a (line 222) | const A { a /* 0_______ */: 1 } = b;
  constant c (line 224) | const A { c: 1 /* 1_______ */ } = d;
  function foo (line 268) | fn foo() {

FILE: tests/output/comments/blocks.f.rs
  function f (line 241) | fn f() {
  function f (line 246) | fn f() {
  function d (line 252) | fn d() {/* _______ */}
  function f (line 254) | fn f() {
  function f (line 265) | fn f() {
  function f (line 270) | fn f() {
  function f (line 275) | fn f() {
  function f (line 280) | fn f() {
  function foo (line 299) | fn foo() {

FILE: tests/output/comments/dangling.f.rs
  function a (line 46) | fn a(/* comment */) {
  type A (line 54) | trait A {/* comment */}
  type A (line 55) | enum A {/* comment */}
  type A (line 56) | struct A(/* comment */);
  type A (line 57) | struct A {/* comment */}
  type A (line 72) | struct A;
  type A (line 76) | struct A;
  type A (line 81) | struct A;
  type A (line 86) | struct A;
  type A (line 92) | struct A;
  type A (line 98) | struct A;

FILE: tests/output/comments/functions.f.rs
  function a (line 1) | fn a(/* _______ */) {}
  function b (line 2) | fn b() {}
  function c (line 3) | fn c(/* _______ */ argA, argB, argC) {}
  function a (line 4) | fn a(a /*_______*/) {}
  function b (line 5) | fn b(a /*_______*/) {}
  function d (line 6) | fn d(a /*_______*/, b /*_______*/, c /*_______*/, d /*_______*/) {}
  function d (line 7) | fn d(a /*_______*/, b /*_______*/, c /*_______*/, d /*_______*/) {/*____...
  function c (line 9) | fn c(a /*_______*/
  function d (line 12) | fn d(
  function e (line 19) | fn e(
  function f1 (line 26) | fn f1 /* _______ */() {}
  function f2 (line 27) | fn f2(/* _______ */) {}
  function f3 (line 28) | fn f3() {/* _______ */}
  function f4 (line 29) | fn f4 /* _______ */(/* _______ */) {/* _______ */}
  function f5 (line 30) | fn f5(/* _______ */ /* _______ */ a) {}
  function f6 (line 31) | fn f6(/* _______ */ a /* _______ */) {}
  function f7 (line 32) | fn f7(/* _______ */ /* _______ */ a) {/* _______ */}

FILE: tests/output/comments/ignore.attr.f.rs
  function no (line 3) | fn no() {
  function f (line 14) | fn   f()    {

FILE: tests/output/comments/ignore.f.rs
  function f (line 25) | fn f() {
  constant bar (line 70) | const A {
  constant bar3 (line 82) | const A {
  constant bar4 (line 87) | const A {
  constant bar5 (line 92) | const A {

FILE: tests/output/comments/ignore.file.f.rs
  function unformatted (line 5) | fn unformatted () {

FILE: tests/output/common/arrays.f.rs
  function isUnusedDiagnostic (line 234) | fn isUnusedDiagnostic(code) {

FILE: tests/output/common/assignments.f.rs
  constant accessibilityModule (line 119) | const A {
  type foo (line 170) | trait foo {
    method bar (line 171) | fn bar() {
  function f (line 215) | async fn f() {
  constant hello__ (line 246) | const t = A {
  constant _id (line 267) | const A { _id: id3 } =

FILE: tests/output/common/binaryish.f.rs
  function f (line 1) | fn f() {
  function f2 (line 11) | fn f2() {
  constant state (line 248) | const obj = A {
  constant state (line 342) | const obj = A {
  function foo (line 354) | fn foo() {
  constant somethingThatsAReallyLongPropName (line 507) | const o = A {

FILE: tests/output/common/chains.f.rs
  function a (line 4) | fn a() {
  function a (line 15) | fn a() {
  function theFunction (line 214) | fn theFunction(action, store) {
  function f (line 231) | fn f() {
  function palindrome (line 331) | fn palindrome(a, b) {
  function HelloWorld (line 376) | fn HelloWorld() {
  type X (line 435) | trait X {
    method y (line 436) | fn y() {
  function assert (line 485) | fn assert(pet) {
  function someFunction (line 644) | fn someFunction(url) {
  type A (line 732) | trait A {
    method compose (line 733) | fn compose() {

FILE: tests/output/common/chains.last-argument-expansion.f.rs
  function searchUsers (line 1) | crate fn searchUsers(action) {
  function mySagas (line 128) | fn mySagas() {
  function mySagas2 (line 137) | fn mySagas2() {

FILE: tests/output/common/closures.f.rs
  function a (line 1) | fn a() {

FILE: tests/output/common/destructuring.f.rs
  function f (line 14) | fn f(A { data: A { name } }) {}

FILE: tests/output/common/types.f.rs
  constant anodyneCondosMalateOverateRetinol (line 165) | const bifornCringerMoshedPerplexSawder =
  function foo (line 185) | fn foo() {
  constant listAuthorizedSitesForDefaultHandler (line 192) | pub const listAuthorizedSitesForDefaultHandler: ListAuthorizedSitesForHa...
  function countriesReceived (line 195) | pub fn countriesReceived(countries: Array<Country>) -> CountryActionType {
  constant findByDate (line 202) | const findByDate: Resolver<void, [Recipe], D> = |_, A { date }, A { req ...
  constant findByDate (line 207) | const findByDate: Resolver<void, [Recipe], D> = |_, A { date }, A { req }|
  constant durabilityMetricsSelectable (line 210) | const durabilityMetricsSelectable: Immutable::OrderedSet<SomeReportingMe...
  constant enviromentProdValues (line 214) | pub(crate) const enviromentProdValues: EnvironmentValues =
  constant myLongVariableName (line 225) | const myLongVariableName: dyn MyLongTypeName + null =
  constant firestorePersonallyIdentifiablePaths (line 231) | const firestorePersonallyIdentifiablePaths: Array<Collections::Users::En...
  constant call (line 234) | const foo = call::<
  constant call (line 245) | const foo = call::<
  constant map (line 253) | const map: Map<
  type X (line 283) | type X = fn(
  constant firestorePersonallyIdentifiablePaths (line 308) | const firestorePersonallyIdentifiablePaths: Array<
  constant SUPPORTED_VEHICLE_TYPES (line 312) | crate const SUPPORTED_VEHICLE_TYPES: Array<Collections::VehiclesStates::...
  type AddAssetHtmlPlugin (line 315) | pub trait AddAssetHtmlPlugin {
    method apply (line 316) | fn apply(compiler: WebpackCompilerType) {
  constant Array (line 372) | const baz = Array::<
  type RequestNextDealAction (line 377) | crate type RequestNextDealAction =
  constant ffff (line 381) | const ffff: fn(typee: Type) -> Provider<Prop> = memoize(

FILE: tests/output/issues/0.f.rs
  type Inner (line 26) | struct Inner {
  function bar (line 44) | fn bar() {
  type S1 (line 52) | struct S1 {
  function main (line 61) | fn main() {
  type S (line 73) | struct S {
    method f (line 878) | fn f() {
  function f (line 103) | fn f() {
  function f (line 136) | fn f() {
  function f (line 147) | fn f() {
  function f (line 158) | fn f() {
  function delete_upload_session (line 163) | pub fn delete_upload_session(&self, sess: &UploadSession) -> Result<()> {
  function very_very_very_very_very_long_fun_name (line 171) | fn very_very_very_very_very_long_fun_name(x: i32) -> Vec<i32> {
  function main (line 175) | fn main() {
  function f (line 190) | fn f() {
  function main (line 243) | fn main() {
  function speak_raw (line 286) | fn speak_raw(
  function f (line 333) | fn f() -> Vec<u32> {
  function foo (line 369) | fn foo() {
  type Iter (line 401) | pub type Iter<'a, D> =
  type Iter (line 406) | pub type Iter<'a, D>: BoundDoubleEndedIterator<Item = SomethingSomething...
  type Iter (line 410) | pub type Iter<'a, D>: BoundDoubleEndedIterator<Item = SomethingSomething...
  function f1 (line 433) | fn f1() -> Box<
  function f2 (line 439) | fn f2() -> Box<
  method parse (line 457) | pub fn parse(value: &str) -> Option<Self> {
  method to_str (line 468) | pub fn to_str(&self) -> &'static str {
  type PrettyPrinter (line 478) | pub trait PrettyPrinter<'tcx>: Printer<
  type PrettyPrinter (line 490) | pub trait PrettyPrinter<'tcx>: Printer<
  type X (line 506) | struct X<'a>(
    constant SOMETHING (line 390) | pub const SOMETHING: usize =
    method ident (line 2227) | pub fn ident(&self) -> Option<Ident> {
  type Foo (line 513) | enum Foo {
    method foo (line 845) | fn foo(&self) -> Box<FnMut(i64) -> i64> {
    method foo (line 1080) | fn foo() {
    method map_pixel_to_coords (line 1836) | fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector...
    method stuff (line 2029) | fn stuff(data: Leet) -> Result<Self> {
  type Kilometers (line 523) | type Kilometers = /*comment*/ i32;
  function test_datetime (line 526) | fn test_datetime() {
  function main (line 546) | fn main() {
  type Bar (line 552) | struct Bar(());
  type Foo (line 553) | struct Foo(Bar);
    method foo (line 845) | fn foo(&self) -> Box<FnMut(i64) -> i64> {
    method foo (line 1080) | fn foo() {
    method map_pixel_to_coords (line 1836) | fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector...
    method stuff (line 2029) | fn stuff(data: Leet) -> Result<Self> {
  function main (line 555) | fn main() {
  function main (line 592) | fn main() {
  function test (line 602) | fn test() {
  function main (line 606) | fn main() {
  function main (line 616) | fn main() {
  function printsomething (line 621) | fn printsomething() {
  constant USAGE (line 628) | const USAGE: &'static str =
  function main (line 643) | fn main() {
  constant USAGE (line 657) | const USAGE: &'static str = "
  function main (line 773) | fn main() {
  function default_user_agent_string (line 823) | fn default_user_agent_string(agent: UserAgent) -> String {
  function uumain (line 867) | pub fn uumain(args: Vec<String>) -> i32 {
  function foo (line 931) | fn foo() -> () {}
  function main (line 969) | fn main() {
  type Foo (line 985) | struct Foo {
    method foo (line 845) | fn foo(&self) -> Box<FnMut(i64) -> i64> {
    method foo (line 1080) | fn foo() {
    method map_pixel_to_coords (line 1836) | fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector...
    method stuff (line 2029) | fn stuff(data: Leet) -> Result<Self> {
  type Foo (line 991) | struct Foo {
    method foo (line 845) | fn foo(&self) -> Box<FnMut(i64) -> i64> {
    method foo (line 1080) | fn foo() {
    method map_pixel_to_coords (line 1836) | fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector...
    method stuff (line 2029) | fn stuff(data: Leet) -> Result<Self> {
  type Ctx (line 1035) | type Ctx = C;
  type Event (line 1036) | type Event = Vec<Target>;
  function main (line 1045) | fn main() {
  function build_sorted_static_get_entry_names (line 1049) | fn build_sorted_static_get_entry_names(
  function main (line 1059) | fn main() {
  function f (line 1068) | fn f() -> Box<
  type Foo (line 1075) | trait Foo {}
    method foo (line 845) | fn foo(&self) -> Box<FnMut(i64) -> i64> {
    method foo (line 1080) | fn foo() {
    method map_pixel_to_coords (line 1836) | fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector...
    method stuff (line 2029) | fn stuff(data: Leet) -> Result<Self> {
  type Bar (line 1076) | struct Bar {}
  function test (line 1085) | fn test() {
  function main (line 1101) | fn main() {
  function handles_mid_demangling (line 1113) | fn handles_mid_demangling() {
  function main (line 1123) | fn main() {
  function test (line 1128) | fn test<const T: i8>() -> i8 {
  function main (line 1148) | fn main() {
  function foo (line 1159) | pub fn foo(config: &Config) {
  function main (line 1172) | fn main() {
  function main (line 1182) | fn main() {
  function main (line 1387) | fn main() {
  function x (line 1825) | fn x() {
  function GetConsoleHistoryInfo (line 1850) | pub fn GetConsoleHistoryInfo(
  function variadic_fn (line 1853) | pub fn variadic_fn(
  function deconstruct (line 1860) | fn deconstruct(
  function needs_paren (line 1883) | fn needs_paren(op: AssocOp, other: AssocOp, dir: Associativity) -> bool {
  function peel_blocks (line 1892) | pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
  function target_features (line 1909) | pub fn target_features(sess: &Session) -> Vec<Symbol> {
  function a (line 1927) | fn a() {
  function g (line 1930) | fn g<'a>(&self, x: usize, y: usize) -> Box<dyn (Fn(bool) -> usize) + 'a> {
  type Compose (line 1935) | struct Compose<F, G>(F, G);
  type Output (line 1940) | type Output = G::Output;
  function call_once (line 1941) | extern "rust-call" fn call_once(self, (x,): (T,)) -> G::Output {
  function build_sorted_static_get_entry_names (line 1946) | fn build_sorted_static_get_entry_names(
  function qcxbfds (line 1955) | fn qcxbfds() {
  constant GATED_CFGS (line 1985) | const GATED_CFGS: &[GatedCfg] = &[
  method c (line 2061) | fn c() {}
  function clock (line 2096) | fn clock() {
  function shutdown (line 2118) | pub(super) fn shutdown(self) {
  function inlining_last_if_else_block_is_awkward (line 2129) | fn inlining_last_if_else_block_is_awkward() {
  function vtable (line 2143) | pub(super) fn vtable<T: Future, S: Schedule>() -> &'static Vtable {
  type Float (line 2166) | pub trait Float: Copy +
    constant BITS (line 2183) | const BITS: usize;
  function space_before_where (line 2188) | fn space_before_where</**/>(_: F) where F: X {}
  function noop_visit_constraint (line 2215) | pub fn noop_visit_constraint<T: MutVisitor>(
  type Foo (line 2284) | pub enum Foo {
    method foo (line 845) | fn foo(&self) -> Box<FnMut(i64) -> i64> {
    method foo (line 1080) | fn foo() {
    method map_pixel_to_coords (line 1836) | fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector...
    method stuff (line 2029) | fn stuff(data: Leet) -> Result<Self> {
  function c (line 2300) | fn c() {}
  type Expect_Parenthesized_dyn (line 2320) | type Expect_Parenthesized_dyn = Pin<&mut (dyn Future<Output = T> + Send)>;

FILE: tests/output/issues/21/fn_comment.f.rs
  function eof (line 1) | fn eof() {}

FILE: tests/output/issues/21/fn_fn.f.rs
  function eof1 (line 1) | fn eof1() {}
  function eof2 (line 2) | fn eof2() {}

FILE: tests/output/issues/21/fn_ln.f.rs
  function eof (line 1) | fn eof() {}

FILE: tests/output/issues/21/ln_fn_ln.f.rs
  function eof (line 1) | fn eof() {}

FILE: tests/output/issues/22.f.rs
  function preserve_last_semicolon (line 1) | fn preserve_last_semicolon() {
  function a (line 10) | fn a() {
  function b (line 15) | fn b() {
  function c (line 27) | fn c() {
  function d (line 33) | fn d() {
  function e (line 38) | fn e() {
  function f (line 43) | fn f() {
  function g (line 48) | fn g() {
  function h (line 54) | fn h() {
  function i (line 61) | fn i() {
  function inner_attr (line 68) | fn inner_attr() {

FILE: tests/output/issues/25.f.rs
  function nums (line 2) | fn nums() {
  function f (line 33) | fn f() {}

FILE: tests/output/styling/needsSemi.f.rs
  function f (line 1) | fn f() {

FILE: tests/samples/comments/assignment.rs
  constant a (line 207) | const A{ a /* 0_______ */ : 1 } = b;
  function foo (line 253) | fn foo() {

FILE: tests/samples/comments/blocks.rs
  function f (line 185) | fn f() {
  function f (line 190) | fn f() {
  function d (line 196) | fn d() {
  function f (line 200) | fn f() {
  function f (line 212) | fn f()
  function f (line 218) | fn f() // _______
  function f (line 223) | fn f() { // _______
  function f (line 227) | fn f() {
  function foo (line 247) | fn foo() {

FILE: tests/samples/comments/dangling.rs
  function a (line 37) | fn a(/* comment */) /* comment */ {/* comment */}
  type A (line 42) | trait A {/* comment */}
  type A (line 43) | enum A {/* comment */}
  type A (line 44) | struct A(/* comment */)
  type A (line 45) | struct A{/* comment */}

FILE: tests/samples/comments/functions.rs
  function a (line 2) | fn a(/* _______ */) {}
  function b (line 3) | fn b() {}
  function c (line 4) | fn c(/* _______ */ argA, argB, argC) {}
  function a (line 5) | fn a(a /*_______*/) {}
  function b (line 6) | fn b(a /*_______*/
  function d (line 8) | fn d(
  function d (line 14) | fn d(
  function c (line 21) | fn c(a /*_______*/
  function d (line 24) | fn d(
  function e (line 31) | fn e(
  function f1 (line 38) | fn f1 /* _______ */() {}
  function f2 (line 39) | fn f2 (/* _______ */) {}
  function f3 (line 40) | fn f3 () /* _______ */ {}
  function f4 (line 41) | fn f4 /* _______ */(/* _______ */) /* _______ */ {}
  function f5 (line 42) | fn f5 /* _______ */(/* _______ */ a) {}
  function f6 (line 43) | fn f6 /* _______ */(a /* _______ */) {}
  function f7 (line 44) | fn f7 /* _______ */(/* _______ */ a) /* _______ */ {}

FILE: tests/samples/comments/ignore.attr.rs
  function no (line 3) | fn no() {
  function f (line 14) | fn   f()    {

FILE: tests/samples/comments/ignore.file.rs
  function unformatted (line 5) | fn unformatted () {

FILE: tests/samples/comments/ignore.rs
  function f (line 26) | fn f() {

FILE: tests/samples/common/arrays.rs
  function isUnusedDiagnostic (line 147) | fn isUnusedDiagnostic(code) {

FILE: tests/samples/common/assignments.rs
  constant accessibilityModule (line 97) | const A { accessibilityModule: FooAccessibilityModule, accessibilityModu...
  type foo (line 133) | trait foo {
    method bar (line 134) | fn bar() {
  function f (line 162) | async fn f() {
  constant _id (line 211) | const A {_id:id3} = data.createTestMessageWithAReallyLongName.someVeryLo...

FILE: tests/samples/common/binaryish.rs
  function f (line 1) | fn f() {
  function f2 (line 7) | fn f2() {
  constant state (line 232) | const obj = A {
  constant state (line 322) | const obj = A {
  function foo (line 334) | fn foo() {
  constant somethingThatsAReallyLongPropName (line 439) | const o = A {

FILE: tests/samples/common/chains.last-argument-expansion.rs
  function searchUsers (line 1) | crate fn searchUsers(action) {
  function mySagas (line 109) | fn mySagas() {
  function mySagas2 (line 121) | fn mySagas2() {

FILE: tests/samples/common/chains.rs
  function a (line 4) | fn a() {
  function a (line 14) | fn a() {
  function f (line 166) | fn f() {
  function assert (line 403) | fn assert(pet) {
  function someFunction (line 543) | fn someFunction(url) {
  type A (line 625) | trait A {
    method compose (line 626) | fn compose() {

FILE: tests/samples/common/closures.rs
  function a (line 1) | fn a() {

FILE: tests/samples/common/destructuring.rs
  function f (line 12) | fn f(A { data: A { name } }) {}

FILE: tests/samples/common/types.rs
  constant listAuthorizedSitesForDefaultHandler (line 151) | pub const listAuthorizedSitesForDefaultHandler: ListAuthorizedSitesForHa...
  function countriesReceived (line 153) | pub fn countriesReceived(countries: Array<Country>) -> CountryActionType {
  constant findByDate (line 160) | const findByDate: Resolver<void, [Recipe], D> =
  constant findByDate (line 166) | const findByDate: Resolver<void, [Recipe], D> =
  constant durabilityMetricsSelectable (line 169) | const durabilityMetricsSelectable: Immutable::OrderedSet<
  constant enviromentProdValues (line 173) | pub(crate) const enviromentProdValues: EnvironmentValues = assign::<Envi...
  constant myLongVariableName (line 183) | const myLongVariableName: MyLongTypeName + null = myLongFunctionCallHere();
  constant firestorePersonallyIdentifiablePaths (line 188) | const firestorePersonallyIdentifiablePaths: Array<Collections::Users::En...
  constant call (line 190) | const foo =
  constant call (line 194) | const foo =
  constant map (line 203) | const map: Map<Function, FunctionFunctionFunctionFunctionffFunction> =
  type X (line 232) | type X = fn(options: AbstractCompositeThingamabobberFactoryProvidereeeee...
  constant firestorePersonallyIdentifiablePaths (line 265) | const firestorePersonallyIdentifiablePaths: Array<
  constant SUPPORTED_VEHICLE_TYPES (line 269) | crate const SUPPORTED_VEHICLE_TYPES: Array<
  type AddAssetHtmlPlugin (line 273) | pub trait AddAssetHtmlPlugin {
    method apply (line 274) | fn apply(compiler: WebpackCompilerType) {
  constant Array (line 314) | const baz = Array::<
  type RequestNextDealAction (line 319) | crate type RequestNextDealAction = BaseAction<DealsActionTypes::REQUEST_...
  constant ffff (line 322) | const ffff: fn(typee: Type) -> Provider<Prop> = memoize(|ty: ObjectType|...

FILE: tests/samples/issues/0.rs
  type Inner (line 23) | struct Inner {
  function bar (line 36) | fn bar() {
  type S1 (line 43) | struct S1 {
  function main (line 52) | fn main() {
  type S (line 63) | struct S {
    method f (line 685) | fn f() {
  function f (line 90) | fn f() {
  function f (line 117) | fn f() {
  function f (line 124) | fn f() {
  function f (line 134) | fn f() {
  function delete_upload_session (line 139) | pub fn delete_upload_session(&self, sess: &UploadSession) -> Result<()> {
  function very_very_very_very_very_long_fun_name (line 149) | fn very_very_very_very_very_long_fun_name(x: i32) -> Vec<i32> {
  function main (line 153) | fn main() {
  function f (line 168) | fn f() {
  function main (line 214) | fn main() {
  function speak_raw (line 241) | fn speak_raw(
  function f (line 263) | fn f() -> Vec<u32> {
  function foo (line 301) | fn foo() {
  type Iter (line 319) | pub type Iter<'a, D> = impl DoubleEndedIterator<Item = (SomethingSomethi...
  type Iter (line 321) | pub type Iter<'a, D>: BoundDoubleEndedIterator<Item = (SomethingSomethin...
  type Iter (line 323) | pub type Iter<'a, D>: BoundDoubleEndedIterator<Item = (SomethingSomethin...
  function f1 (line 332) | fn f1() -> Box<
  function f2 (line 339) | fn f2() -> Box<
  method parse (line 357) | pub fn parse(value: &str) -> Option<Self> {
  method to_str (line 368) | pub fn to_str(&self) -> &'static str {
  type PrettyPrinter (line 378) | pub trait PrettyPrinter<'tcx>:
  type PrettyPrinter (line 391) | pub trait PrettyPrinter<'tcx>:
  type X (line 406) | struct X<'a>(
    constant SOMETHING (line 309) | pub const SOMETHING: usize = mem::size_of::<i64>() // field A
    method ident (line 1836) | pub fn ident(&self) -> Option<Ident> {
  type Foo (line 412) | enum Foo {
    method foo (line 647) | fn foo(&self) -> Box<FnMut(i64) -> i64> {
    method foo (line 871) | fn foo() {
    method map_pixel_to_coords (line 1511) | fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector...
    method stuff (line 1667) | fn stuff(data: Leet) -> Result<Self> {
  type Kilometers (line 423) | type Kilometers =/*comment*/i32;
  function test_datetime (line 426) | fn test_datetime() {
  function main (line 445) | fn main() {
  type Bar (line 451) | struct Bar(());
  type Foo (line 452) | struct Foo(Bar);
    method foo (line 647) | fn foo(&self) -> Box<FnMut(i64) -> i64> {
    method foo (line 871) | fn foo() {
    method map_pixel_to_coords (line 1511) | fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector...
    method stuff (line 1667) | fn stuff(data: Leet) -> Result<Self> {
  function main (line 454) | fn main() {
  function main (line 491) | fn main() {
  function test (line 509) | fn test() {
  function main (line 513) | fn main() {
  function main (line 521) | fn main() {
  function printsomething (line 525) | fn printsomething() {
  constant USAGE (line 534) | const USAGE: &'static str = "
  function main (line 548) | fn main() {
  constant USAGE (line 560) | const USAGE: &'static str = "
  function main (line 593) | fn main() {
  function default_user_agent_string (line 628) | fn default_user_agent_string(agent: UserAgent) -> String {
  function uumain (line 675) | pub fn uumain(args: Vec<String>) -> i32 {
  function foo (line 735) | fn foo() -> () where {}
  function main (line 765) | fn main() {
  type Foo (line 779) | struct Foo {
    method foo (line 647) | fn foo(&self) -> Box<FnMut(i64) -> i64> {
    method foo (line 871) | fn foo() {
    method map_pixel_to_coords (line 1511) | fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector...
    method stuff (line 1667) | fn stuff(data: Leet) -> Result<Self> {
  type Foo (line 785) | struct Foo {
    method foo (line 647) | fn foo(&self) -> Box<FnMut(i64) -> i64> {
    method foo (line 871) | fn foo() {
    method map_pixel_to_coords (line 1511) | fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector...
    method stuff (line 1667) | fn stuff(data: Leet) -> Result<Self> {
  type Ctx (line 827) | type Ctx = C;
  type Event (line 828) | type Event = Vec<Target>;
  function main (line 837) | fn main() {
  function build_sorted_static_get_entry_names (line 841) | fn build_sorted_static_get_entry_names(
  function main (line 852) | fn main() {
  function f (line 861) | fn f() -> Box<
  type Foo (line 866) | trait Foo where {}
    method foo (line 647) | fn foo(&self) -> Box<FnMut(i64) -> i64> {
    method foo (line 871) | fn foo() {
    method map_pixel_to_coords (line 1511) | fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector...
    method stuff (line 1667) | fn stuff(data: Leet) -> Result<Self> {
  type Bar (line 867) | struct Bar where {}
  function test (line 876) | fn test() {
  function main (line 891) | fn main() {
  function handles_mid_demangling (line 902) | fn handles_mid_demangling() {
  function main (line 909) | fn main() {
  function test (line 914) | fn test<const T: i8>() -> i8 {
  function main (line 938) | fn main() {
  function foo (line 948) | pub fn foo(config: &Config) {
  function main (line 962) | fn main() {
  function main (line 972) | fn main() {
  function main (line 1158) | fn main() {
  function x (line 1505) | fn x() {
  function GetConsoleHistoryInfo (line 1519) | pub fn GetConsoleHistoryInfo(console_history_info: *mut ConsoleHistoryIn...
  function variadic_fn (line 1520) | pub fn variadic_fn(first_parameter: FirstParameterType,
  function deconstruct (line 1525) | fn deconstruct(foo: Bar) -> (SocketAddr, Header, Method, RequestUri, Htt...
  function needs_paren (line 1539) | fn needs_paren(op: AssocOp, other: AssocOp, dir: Associativity) -> bool {
  function peel_blocks (line 1548) | pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
  function target_features (line 1564) | pub fn target_features(sess: &Session) -> Vec<Symbol> {
  function a (line 1581) | fn a() { Arc::new(|_| { Ok(()) })}
  function g (line 1582) | fn g<'a>(&self, x: usize, y:usize) -> Box<dyn Fn(bool) -> usize + 'a> {
  type Compose (line 1587) | struct Compose<F, G>(F, G);
  type Output (line 1593) | type Output = G::Output;
  function call_once (line 1594) | extern "rust-call" fn call_once(self, (x,): (T,)) -> G::Output {
  function build_sorted_static_get_entry_names (line 1599) | fn build_sorted_static_get_entry_names(
  function qcxbfds (line 1609) | fn qcxbfds() {
  constant GATED_CFGS (line 1633) | const GATED_CFGS: &[GatedCfg] = &[
  method c (line 1696) | fn c() {}
  function clock (line 1735) | fn clock(){
  function shutdown (line 1744) | pub(super) fn shutdown(self) {
  function inlining_last_if_else_block_is_awkward (line 1752) | fn inlining_last_if_else_block_is_awkward() {
  function vtable (line 1766) | pub(super) fn vtable<T: Future, S: Schedule>() -> &'static Vtable {
  type Float (line 1788) | pub trait Float:
    constant BITS (line 1805) | const BITS: usize;
  function space_before_where (line 1810) | fn space_before_where</**/>(_: F) where F: X {}
  function noop_visit_constraint (line 1824) | pub fn noop_visit_constraint<T: MutVisitor>(
  type Foo (line 1893) | pub enum Foo {
    method foo (line 647) | fn foo(&self) -> Box<FnMut(i64) -> i64> {
    method foo (line 871) | fn foo() {
    method map_pixel_to_coords (line 1511) | fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector...
    method stuff (line 1667) | fn stuff(data: Leet) -> Result<Self> {
  function c (line 1908) | fn c() {}
  type Expect_Parenthesized_dyn (line 1929) | type Expect_Parenthesized_dyn = Pin<&mut (dyn Future<Output = T> + Send)>;

FILE: tests/samples/issues/21/fn_comment.rs
  function eof (line 1) | fn eof() {}

FILE: tests/samples/issues/21/fn_fn.rs
  function eof1 (line 1) | fn eof1() {}
  function eof2 (line 2) | fn eof2() {}

FILE: tests/samples/issues/21/fn_ln.rs
  function eof (line 1) | fn eof() {}

FILE: tests/samples/issues/21/ln_fn_ln.rs
  function eof (line 2) | fn eof(){}

FILE: tests/samples/issues/22.rs
  function preserve_last_semicolon (line 1) | fn preserve_last_semicolon() {
  function a (line 10) | fn a() {
  function b (line 14) | fn b() {
  function c (line 26) | fn c() {
  function d (line 31) | fn d() {
  function e (line 35) | fn e() {
  function f (line 39) | fn f() {
  function g (line 46) | fn g() {
  function h (line 54) | fn h() {
  function i (line 59) | fn i() {
  function inner_attr (line 64) | fn inner_attr() {

FILE: tests/samples/issues/25.rs
  function nums (line 2) | fn nums() {
  function f (line 37) | fn f() {}

FILE: tests/samples/styling/needsSemi.rs
  function f (line 1) | fn f() {
Condensed preview — 202 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (850K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/Bug_report.yml",
    "chars": 586,
    "preview": "name: ✨ Formatting\ndescription: Issues for incorrect or unreadable code\nbody:\n  - type: textarea\n    id: code\n    attrib"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 184,
    "preview": "contact_links:\n  - name: Github discussion board\n    url: https://github.com/jinxdash/prettier-plugin-rust/discussions\n "
  },
  {
    "path": ".gitignore",
    "chars": 156,
    "preview": "# npm\nnode_modules\n\n# cache\n/.rollup.cache/\n*.tsbuildinfo\n\n# debug\n*.temp.*\n*.temp/\n\n# repl\n/repl/\n\n# build\n/index.*\n/ex"
  },
  {
    "path": ".gitmodules",
    "chars": 99,
    "preview": "[submodule \"ext/jinx-rust\"]\n\tpath = ext/jinx-rust\n\turl = https://github.com/jinxdash/jinx-rust.git\n"
  },
  {
    "path": ".npmrc",
    "chars": 31,
    "preview": "link-workspace-packages = false"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 550,
    "preview": "// A launch configuration that compiles the extension and then opens it inside a new window\n// Use IntelliSense to learn"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 91,
    "preview": "{\n  \"npm.exclude\": [\n    \"**/ext/jinx-rust\"\n  ],\n  \"files.exclude\": {\n    \"ext\": true\n  }\n}"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 2149,
    "preview": "# Prettier Rust Changelog\n\n## Unreleased\n\n- fix: clear some issues related to formatting `#[attr]` like full-on expressi"
  },
  {
    "path": "LICENSE",
    "chars": 1130,
    "preview": "MIT License\n\nCopyright (c) 2022-present jinxdash <jinxdash.github@gmail.com> (https://github.com/jinxdash)\n\nPermission i"
  },
  {
    "path": "README.md",
    "chars": 9905,
    "preview": "<div align=\"center\">\n  <img alt=\"Prettier Rust\" height=\"256px\" src=\"https://user-images.githubusercontent.com/109366411/"
  },
  {
    "path": "crate/Cargo.toml",
    "chars": 501,
    "preview": "[package]\nname = \"prettier\"\nversion = \"0.1.5\"\nedition = \"2021\"\nreadme = \"README.md\"\nlicense = \"MIT\"\ndescription = \"Opini"
  },
  {
    "path": "crate/README.md",
    "chars": 269,
    "preview": "# Prettier crate\n\n_Reserved_ (August 2022)\n\nhttps://github.com/jinxdash/prettier-plugin-rust\n\n- _(todo) Crate provides P"
  },
  {
    "path": "crate/src/main.rs",
    "chars": 45,
    "preview": "fn main() {\n    println!(\"Hello, world!\");\n}\n"
  },
  {
    "path": "extension/LICENSE",
    "chars": 1130,
    "preview": "MIT License\n\nCopyright (c) 2022-present jinxdash <jinxdash.github@gmail.com> (https://github.com/jinxdash)\n\nPermission i"
  },
  {
    "path": "extension/README.md",
    "chars": 8467,
    "preview": "<div align=\"center\">\n  <img alt=\"Prettier Rust\" height=\"256px\" src=\"https://user-images.githubusercontent.com/109366411/"
  },
  {
    "path": "extension/package.json",
    "chars": 1339,
    "preview": "{\n\t\"icon\": \"icon.png\",\n\t\"name\": \"prettier-rust\",\n\t\"publisher\": \"jinxdash\",\n\t\"displayName\": \"Prettier - Code formatter (R"
  },
  {
    "path": "extension/src/index.ts",
    "chars": 3422,
    "preview": "import { rs } from \"jinx-rust\";\nimport path from \"node:path\";\nimport prettier, { Config, resolveConfig } from \"prettier\""
  },
  {
    "path": "extension/tsconfig.json",
    "chars": 103,
    "preview": "{\n\t\"extends\": \"../tsconfig.base.json\",\n\t\"compilerOptions\": { \"rootDir\": \"src\" },\n\t\"include\": [\"src\"]\n}\n"
  },
  {
    "path": "package.json",
    "chars": 1479,
    "preview": "{\n\t\"name\": \"prettier-plugin-rust\",\n\t\"version\": \"0.1.9\",\n\t\"description\": \"Prettier plugin for Rust\",\n\t\"repository\": {\n\t\t\""
  },
  {
    "path": "pnpm-workspace.yaml",
    "chars": 37,
    "preview": "packages:\n  - 'ext/*'\n  - 'extension'"
  },
  {
    "path": "scripts/build.ts",
    "chars": 514,
    "preview": "import { build } from \"tsup\";\nimport { createStripPlugin } from \"../ext/jinx-rust/scripts/utils/build\";\n\nawait build({\n\t"
  },
  {
    "path": "scripts/dev.format.ts",
    "chars": 731,
    "preview": "import prettier from \"prettier\";\nimport { for_each_rs_file } from \"../ext/jinx-rust/scripts/utils/common\";\nimport { upda"
  },
  {
    "path": "scripts/dev.repl.ts",
    "chars": 296,
    "preview": "import { createASTtoJSONPrinter, createPrettierPrinter, rs_createREPL } from \"../ext/jinx-rust/scripts/utils\";\nimport { "
  },
  {
    "path": "scripts/tsconfig.json",
    "chars": 227,
    "preview": "{\n\t\"extends\": \"../tsconfig.base.json\",\n\t\"include\": [\"../tests\", \"../scripts\"],\n\t\"references\": [{ \"path\": \"../tsconfig.js"
  },
  {
    "path": "src/format/comments.ts",
    "chars": 27425,
    "preview": "import { CommentOrDocComment, LocArray, Node, NodeType, NodeWithBodyOrCases } from \"jinx-rust\";\nimport {\n\tend,\n\tgetBodyO"
  },
  {
    "path": "src/format/complexity.ts",
    "chars": 8487,
    "preview": "import {\n\tForLtParametersBody,\n\tFunctionSpread,\n\tGenericParameterDeclaration,\n\tMaybeGenericArgsTarget,\n\tMissingNode,\n\tNo"
  },
  {
    "path": "src/format/core.ts",
    "chars": 74918,
    "preview": "import {\n\tAndExpression,\n\tAttributeOrDocComment,\n\tBreakExpression,\n\tCallExpression,\n\tClosureFunctionExpression,\n\tCompari"
  },
  {
    "path": "src/format/external.ts",
    "chars": 3346,
    "preview": "import { Attribute, AttributeOrDocComment, Comment, DocCommentAttribute, LocArray, MemberExpression, Node, SourceFile } "
  },
  {
    "path": "src/format/plugin.ts",
    "chars": 12259,
    "preview": "import { AttributeOrComment, IfBlockExpression, Node, Program, rs } from \"jinx-rust\";\nimport {\n\tArrayProps,\n\tBoolProps,\n"
  },
  {
    "path": "src/format/printer.ts",
    "chars": 21818,
    "preview": "import { DelimKind, Node, NodeType, NTMap } from \"jinx-rust\";\nimport {\n\tgetDelimChars,\n\thasSuffix,\n\tis_ArrayOrTupleLiter"
  },
  {
    "path": "src/format/styling.ts",
    "chars": 17857,
    "preview": "import {\n\tClosureFunctionExpression,\n\tComparisonExpression,\n\tConditionExpression,\n\tEnumDeclaration,\n\tEnumMemberStructDec"
  },
  {
    "path": "src/index.ts",
    "chars": 281,
    "preview": "import { plugin } from \"./format/plugin\";\n\nexport default plugin;\nexport const languages = plugin.languages;\nexport cons"
  },
  {
    "path": "src/transform/custom/attribute.ts",
    "chars": 2991,
    "preview": "import {\n\tAttrSegment,\n\tCallExpression,\n\tDelimKind,\n\tExpressionPath,\n\tIdentifier,\n\tLiteral,\n\tLocArray,\n\tMacroInvocation,"
  },
  {
    "path": "src/transform/custom/cfg_if.ts",
    "chars": 2501,
    "preview": "import {\n\tDelimGroup,\n\tDelimKind,\n\tIfBlockExpression,\n\tLocArray,\n\tMacroInvocation,\n\tNodeType,\n\tNodeWithBody,\n\trs,\n\tSegme"
  },
  {
    "path": "src/transform/custom/utils.ts",
    "chars": 958,
    "preview": "import { DelimGroup, DelimKind, Identifier, LocArray, PunctuationToken, Segment, TK } from \"jinx-rust\";\nimport { isTK, i"
  },
  {
    "path": "src/transform/index.ts",
    "chars": 15926,
    "preview": "import {\n\tAttribute,\n\tAttributeOrDocComment,\n\tCallExpression,\n\tDelimKind,\n\tExpressionNode,\n\tLocArray,\n\tMacroInvocation,\n"
  },
  {
    "path": "src/utils/common.ts",
    "chars": 9295,
    "preview": "import { createCustomError } from \"./debug\";\n\ndeclare global {\n\tvar console: { log(...args: any[]): void; error(...args:"
  },
  {
    "path": "src/utils/debug.ts",
    "chars": 5019,
    "preview": "import { clamp, color, getTerminalWidth, normPath } from \"./common\";\n\nconst cwd = // @ts-expect-error\n\ttypeof process =="
  },
  {
    "path": "tests/output/comments/assignment.f.rs",
    "chars": 3943,
    "preview": "f1 = |\n  //comment\n  a\n| {};\n\nf2 = |\n  a //comment\n| {};\n\nf3 = |\n  a\n  //comment\n| {};\n\nf4 = // Comment\n  || {};\n\nf5 =\n "
  },
  {
    "path": "tests/output/comments/binaryish.f.rs",
    "chars": 1792,
    "preview": "a = b || /** 5_______ */ c;\n\na = b || /** 6_______ */ c;\n\na =\n  b ||\n  /** 7____________________________________________"
  },
  {
    "path": "tests/output/comments/blocks.f.rs",
    "chars": 3415,
    "preview": "if 0 {\n  0;\n  //\n} else if 0 {\n}\n\nif 1 {\n  /*\n   * _______\n   */\n}\n\nif 1 {\n  // _______\n}\n\nif 1 {\n} else {\n  // _______\n"
  },
  {
    "path": "tests/output/comments/chain.f.rs",
    "chars": 1264,
    "preview": "_\n  .a(a)\n  /* _____________________________________________________________________________ */\n  .a();\n\n_\n  .a(\n    a\n "
  },
  {
    "path": "tests/output/comments/closure.f.rs",
    "chars": 924,
    "preview": "call(|/*_______*/ row| {});\nKEYPAD_NUMBERS.map(\n  |num|\n    // _______\n    1\n);\n\nconst obj = A {\n  f1: /* _______ */ || "
  },
  {
    "path": "tests/output/comments/dangling.f.rs",
    "chars": 1834,
    "preview": "use std::{/* comment */};\n\nmacro_rules! m {/* comment */}\nmacro_rules! m {\n  /* comment */ /* comment */ (/* comment */)"
  },
  {
    "path": "tests/output/comments/file.f.rs",
    "chars": 408,
    "preview": "// This file only\n// has comments. This comment\n// should still exist\n//\n// when printed.\n\n/**\n * @typedef {DataDrivenMa"
  },
  {
    "path": "tests/output/comments/flow.f.rs",
    "chars": 995,
    "preview": "loop {\n  break /* _______ */;\n  continue /* _______ */;\n}\n\n'loop: loop {\n  break /* _______ */ 'loop;\n  break 'loop /* _"
  },
  {
    "path": "tests/output/comments/functions.f.rs",
    "chars": 856,
    "preview": "fn a(/* _______ */) {} // _______\nfn b() {} // _______\nfn c(/* _______ */ argA, argB, argC) {} // _______\nfn a(a /*_____"
  },
  {
    "path": "tests/output/comments/ignore.attr.f.rs",
    "chars": 282,
    "preview": "const baseline = 1 + 1;\n\nfn no() {\n  a(  a  ); #![rustfmt::skip]\n  // _______\n}\n\n#[rustfmt::skip]\nconst a    =   A {\n  b"
  },
  {
    "path": "tests/output/comments/ignore.f.rs",
    "chars": 1491,
    "preview": "const baseline = 1 + 1;\n\n// prettier-ignore\nlet x =\n  \"\" + this.USE + \" \" + this.STRICT + \";\\n\" +\n  this.filterPrefix() "
  },
  {
    "path": "tests/output/comments/ignore.file.f.rs",
    "chars": 155,
    "preview": "\tconst unformatted= 1;\n\n#![rustfmt::skip] // a\n\n fn unformatted () {\n\t struct a {\n\t\t \t\t\t// b\n\t\t c\n\t }\n }\n// source: \"../"
  },
  {
    "path": "tests/output/comments/imports.f.rs",
    "chars": 301,
    "preview": "use list::{\n  // Some item\n  SomeItem /* Comment */,\n  /* Another item */ AnotherItem /* Another Comment */, // Last Ite"
  },
  {
    "path": "tests/output/comments/macro.f.rs",
    "chars": 257,
    "preview": "a!(~ \" {    }  \");\na!(~ // 1\n);\na!(~ {  // 2\n});\n\ncfg_if::cfg_if! {\n  if #[attr] {\n    if 0 {\n    } else {\n      // ERRO"
  },
  {
    "path": "tests/output/comments/multiple.f.rs",
    "chars": 916,
    "preview": "/* _______ */ /* _______ */ /* _______ */ a;\na; /* _______ */ /* _______ */ /* _______ */\na; // _______\na;\n/*1*/ /*2*/ /"
  },
  {
    "path": "tests/output/comments/parens.f.rs",
    "chars": 2289,
    "preview": "!x;\n!(x /* 0 */);\n!(/* 1 */ x);\n!(\n  /* 2 */\n  x\n);\n!(\n  x\n  /* 3 */\n);\n!(\n  x // 4\n);\n\n!(x + y);\n!(x + y /* 5 */);\n!(/*"
  },
  {
    "path": "tests/output/comments/whitespace.f.rs",
    "chars": 874,
    "preview": "/*\n  1\n        */\n/*\n          2\n        */\n/*\n\t\t\t\t\t\t\t\t  3\n        */\n/*\n\n\n\n\n\n\n\n\n  4\n        */\n\n/*\n  5a                "
  },
  {
    "path": "tests/output/common/arrays.f.rs",
    "chars": 7481,
    "preview": "{\n  for srcPath in [src, \"${src}.js\", \"${src}/index\", \"${src}/index.js\"] {\n  }\n}\n{\n  for srcPath in [123, 123_123_123, 1"
  },
  {
    "path": "tests/output/common/assignments.f.rs",
    "chars": 10903,
    "preview": "const computedDescriptionLines =\n  (showConfirm && descriptionLinesConfirming) ||\n  (focused && !loading && descriptionL"
  },
  {
    "path": "tests/output/common/binaryish.f.rs",
    "chars": 10561,
    "preview": "fn f() {\n  const appEntities = getAppEntities(loadObject).filter(\n    |entity|\n      entity &&\n      entity.isInstallAva"
  },
  {
    "path": "tests/output/common/chains.f.rs",
    "chars": 18259,
    "preview": "const thingamabobMetaAlias = path.scope.getProgramParent().path.get(\"body\")\n  [0].node;\n\nfn a() {\n  fn b() {\n    queryTh"
  },
  {
    "path": "tests/output/common/chains.first-argument-expansion.f.rs",
    "chars": 2306,
    "preview": "setTimeout(|| {\n  thing();\n}, 500);\n\n[\"a\", \"b\", \"c\"].reduce(|item, thing| {\n  return thing + \" \" + item;\n}, \"letters:\");"
  },
  {
    "path": "tests/output/common/chains.last-argument-expansion.f.rs",
    "chars": 4735,
    "preview": "crate fn searchUsers(action) {\n  return action\n    .ofType(ActionTypes.SEARCHED_USERS)\n    .map(|| action.payload.query)"
  },
  {
    "path": "tests/output/common/closures.f.rs",
    "chars": 5377,
    "preview": "fn a() {\n  async |x| x\n}\n\n|aaaa| {};\n\nx =\n  |bifornCringerMoshedPerplexSawder|\n  |askTrovenaBeenaDependsRowans, glimseGl"
  },
  {
    "path": "tests/output/common/destructuring.f.rs",
    "chars": 820,
    "preview": "const [one, two @ null, three @ null] = arr;\na = |[s @ 1]| 1;\nconst A { children, .. } = this.props;\n\nconst A { user: A "
  },
  {
    "path": "tests/output/common/members.f.rs",
    "chars": 2179,
    "preview": "(\n  if valid {\n    helper.responseBody(this.currentUser)\n  } else {\n    helper.responseBody(this.defaultUser)\n  }\n).prop"
  },
  {
    "path": "tests/output/common/types.f.rs",
    "chars": 8786,
    "preview": "const bar1 = [1, 2, 3].reduce(|| {\n  return [..carry, value];\n}, [] as unknown as [number]);\n\nconst bar3 = [1, 2, 3].red"
  },
  {
    "path": "tests/output/issues/0.f.rs",
    "chars": 51404,
    "preview": "//! Expect 1 empty line below\n\n//! Expect 0 empty line below\n//! Expect 1 empty line below\n\n1;\n//! Expect 1 empty line b"
  },
  {
    "path": "tests/output/issues/14.f.rs",
    "chars": 64,
    "preview": "[10.0, 10.0, 10.0, 10];\n\n// source: \"../../samples/issues/14.rs\""
  },
  {
    "path": "tests/output/issues/21/fn_comment.f.rs",
    "chars": 77,
    "preview": "fn eof() {}\n// comment\n\n// source: \"../../../samples/issues/21/fn_comment.rs\""
  },
  {
    "path": "tests/output/issues/21/fn_fn.f.rs",
    "chars": 75,
    "preview": "fn eof1() {}\nfn eof2() {}\n\n// source: \"../../../samples/issues/21/fn_fn.rs\""
  },
  {
    "path": "tests/output/issues/21/fn_ln.f.rs",
    "chars": 61,
    "preview": "fn eof() {}\n\n// source: \"../../../samples/issues/21/fn_ln.rs\""
  },
  {
    "path": "tests/output/issues/21/ln_fn_ln.f.rs",
    "chars": 64,
    "preview": "fn eof() {}\n\n// source: \"../../../samples/issues/21/ln_fn_ln.rs\""
  },
  {
    "path": "tests/output/issues/21/mod.f.rs",
    "chars": 58,
    "preview": "mod eof {}\n\n// source: \"../../../samples/issues/21/mod.rs\""
  },
  {
    "path": "tests/output/issues/22.f.rs",
    "chars": 991,
    "preview": "fn preserve_last_semicolon() {\n  if let Some(left) = node.borrow().left.as_ref() {\n    deque.push_back(left.clone());\n  "
  },
  {
    "path": "tests/output/issues/25.f.rs",
    "chars": 635,
    "preview": "#[generator(yield(i32))]\nfn nums() {\n  yield_!(3);\n}\n\n// some extra samples to track changes\n#[// 0\ngenerator(\n  // 1\n  "
  },
  {
    "path": "tests/output/issues/nth-pass.f.1.rs",
    "chars": 547,
    "preview": "// prettier for javascript cannot format those in one pass\n\nreturn (\n  // _______\n  42 * 84 + 2\n);\nreturn (\n  // _______"
  },
  {
    "path": "tests/output/issues/nth-pass.f.rs",
    "chars": 554,
    "preview": "// prettier for javascript cannot format those in one pass\n\nreturn (\n  // _______\n  42 *\n    84 +\n  2\n);\nreturn (\n  // _"
  },
  {
    "path": "tests/output/macros/cfg_if.f.rs",
    "chars": 1090,
    "preview": "[\n  cfg_if! {\n    if #[cfg(def)] {\n      use std;\n      0\n    }\n  },\n  cfg_if! {\n    if #[cfg(def)] {\n      use std;\n   "
  },
  {
    "path": "tests/output/macros/if_chain.f.rs",
    "chars": 472,
    "preview": "[\n  if_chain! {\n\t\tif let Some(a) = b;\n\t\tif let Err(a) = b;\n\t\tlet (a, b) = c;\n\t\tif 1 + 2;\n\t\tthen { d=0; }\n\t},\n  if_chain!"
  },
  {
    "path": "tests/output/macros/matches.f.rs",
    "chars": 180,
    "preview": "[\n  matches!(1 + 1, Some(_)),\n  matches!(1 + 1,   Some(_) | None if 1 + 1 == 2),\n  matches!(1 + 1, | Some(_) | None if 1"
  },
  {
    "path": "tests/output/styling/blockify.f.rs",
    "chars": 343,
    "preview": "[\n  || 0,\n  || {\n    match 0 {\n    }\n  },\n  || (\n    if 0 {\n    }\n  ),\n  || {\n    loop {\n    }\n  },\n  || const {},\n  || "
  },
  {
    "path": "tests/output/styling/canInlineBlockBody.f.rs",
    "chars": 1668,
    "preview": "[\n  {\n    if 0 {\n      0\n    }\n  },\n  {\n    if 0 { 0 } else { 0 }\n  },\n  {\n    while 0 {\n      0;\n    }\n  },\n  {\n    uns"
  },
  {
    "path": "tests/output/styling/needsParens.f.rs",
    "chars": 190,
    "preview": "let (A {} | a() | []) = ();\nif let A {} | a() | [] = () {\n}\n\ntype A: B + C;\ntype A: (impl B + C);\n\ntrait A = B + C;\ntrai"
  },
  {
    "path": "tests/output/styling/needsSemi.f.rs",
    "chars": 120,
    "preview": "fn f() {\n  #[cfg(unix)]\n  {\n    0\n  }\n  #[cfg(windows)]\n  {\n    1\n  }\n}\n\n// source: \"../../samples/styling/needsSemi.rs\""
  },
  {
    "path": "tests/output-ext/errors/foo.rs",
    "chars": 4962,
    "preview": "> Error.toString()\n> \n--------------------------------------------------------------------------------------------------"
  },
  {
    "path": "tests/output-ext/expressions/block.f.rs",
    "chars": 1670,
    "preview": "fn f() {\n  ({ foo.0 }).0 = 0;\n  (async {}).await;\n  async move {\n  }\n  ({ a }).0 += { 0 };\n  try {\n  }\n  match (try {}) "
  },
  {
    "path": "tests/output-ext/expressions/closure.f.rs",
    "chars": 3651,
    "preview": "fn main() {\n  let lam = |(a, ref b, c, ref mut d): (X, X, X, X)| {};\n  let x = |_: ()| ();\n  let y = || x(());\n  let mut"
  },
  {
    "path": "tests/output-ext/expressions/expr.f.rs",
    "chars": 6049,
    "preview": "fn main() {\n  let a = async move {};\n  999999999999999999999999999999999999999999999999999999999999999999999999999999999"
  },
  {
    "path": "tests/output-ext/expressions/flow_expr.f.rs",
    "chars": 1862,
    "preview": "pub fn main() {\n  loop {\n    return ({\n      break;\n    }) as ();\n  }\n  return ();\n  ({\n    return;\n  }) as ();\n  return"
  },
  {
    "path": "tests/output-ext/expressions/ident.f.rs",
    "chars": 561,
    "preview": "fn bare_crate(_: crate::a);\nfn bare_global(_: ::a);\nfn u8(u8: u8) {\n  if u8 != 0u8 {\n  }\n  assert_eq!(8u8, {\n    macro_r"
  },
  {
    "path": "tests/output-ext/expressions/literal.f.rs",
    "chars": 4020,
    "preview": "[\n  ('\\x0A', '\\x0B', '\\x0C', '\\x0D', '\\x20', '\\u{85}', '\\u{A0}'),\n  ('\\u{1680}', '\\u{2000}', '\\u{2001}', '\\u{2002}', '\\u"
  },
  {
    "path": "tests/output-ext/expressions/match.f.rs",
    "chars": 7454,
    "preview": "fn a() {\n  match x {\n  }\n  match () {\n  }\n  match (Sd { x: A, y: () }) {\n  }\n  match *c {\n  }\n  match ((A, ()), ()) {\n  "
  },
  {
    "path": "tests/output-ext/expressions/parens.f.rs",
    "chars": 4640,
    "preview": "fn main() {\n  holds_callable.callable();\n  (holds_callable.callable)();\n  a = {\n    b = c;\n  };\n  mystruct.myfield;\n  fo"
  },
  {
    "path": "tests/output-ext/expressions/precedence.f.rs",
    "chars": 11293,
    "preview": "fn main() {\n  let _: &'static _ = &(|| {\n    let _ = 0;\n    0\n  });\n  let _ = (match c(o.m(), o as T::T) {\n    0 if o::c"
  },
  {
    "path": "tests/output-ext/expressions/range.f.rs",
    "chars": 2214,
    "preview": "fn q() {\n  if let 0..3 = 0 {\n  }\n  if let 0..Y = 0 {\n  }\n  if let X..3 = 0 {\n  }\n  if let X..Y = 0 {\n  }\n\n  if let 0..=3"
  },
  {
    "path": "tests/output-ext/features/arbitrary_enum_discriminant.f.rs",
    "chars": 1474,
    "preview": "#![feature(arbitrary_enum_discriminant)]\n\nenum Enum {\n  Unit = 1,\n  Tuple() = 2,\n  Struct {} = 3,\n}\n#[repr(u8)]\nenum Enu"
  },
  {
    "path": "tests/output-ext/features/associated_type_bounds.f.rs",
    "chars": 341,
    "preview": "#![feature(associated_type_bounds)]\n\ntype X = A<B: C>;\n\nfn f<F>(_: F) where F: for<'a> Trait<Output: 'a> {}\nfn f<'b, F>("
  },
  {
    "path": "tests/output-ext/features/async_closure.f.rs",
    "chars": 127,
    "preview": "#![feature(async_closure)]\n\nconst X = async |x| 0;\n\n// source: \"../../../ext/jinx-rust/tests/samples/features/async_clos"
  },
  {
    "path": "tests/output-ext/features/auto_traits.f.rs",
    "chars": 267,
    "preview": "#![feature(auto_traits)]\n\nauto trait T {}\nunsafe auto trait T {}\npub auto trait T {}\npub unsafe auto trait T {}\n\nauto tr"
  },
  {
    "path": "tests/output-ext/features/const_generics_defaults.f.rs",
    "chars": 1070,
    "preview": "#![feature(const_generics_defaults)]\n\nstruct Foo<const N: usize = 1, const N2: usize = 2>;\nstruct Bar<const N: usize, co"
  },
  {
    "path": "tests/output-ext/features/const_trait_impl.f.rs",
    "chars": 676,
    "preview": "#![feature(const_trait_impl)]\n\nimpl const T for S {}\nimpl<T> const T for S {}\n\nimpl const T for S {\n  #![attr]\n}\n\n#[attr"
  },
  {
    "path": "tests/output-ext/features/decl_macro.f.rs",
    "chars": 111,
    "preview": "#![feature(decl_macro)]\n\nmacro m() {}\n\n// source: \"../../../ext/jinx-rust/tests/samples/features/decl_macro.rs\""
  },
  {
    "path": "tests/output-ext/features/destructuring_assignment.f.rs",
    "chars": 2201,
    "preview": "#![feature(destructuring_assignment)]\n\nfn main() {\n  _ = 1;\n  _ = DropRecorder(1);\n  _val = DropRecorder(2);\n  (_,) = (1"
  },
  {
    "path": "tests/output-ext/features/generators.f.rs",
    "chars": 236,
    "preview": "#![feature(generators)]\n\n[\n  static move || {\n    let a = A::<Box<dyn D>> { b: E::r() };\n    yield ();\n  },\n  |_| {\n    "
  },
  {
    "path": "tests/output-ext/features/if_let_guard.f.rs",
    "chars": 379,
    "preview": "#![feature(if_let_guard)]\n\nfn main() {\n  match e {\n    A(ref u) if let a = b && let c = d => {}\n    A(ref u) if let x { "
  },
  {
    "path": "tests/output-ext/features/inline_const.f.rs",
    "chars": 128,
    "preview": "#![feature(inline_const_pat)]\n\nfn f() {\n  const {}\n}\n\n// source: \"../../../ext/jinx-rust/tests/samples/features/inline_c"
  },
  {
    "path": "tests/output-ext/features/inline_const_pat.f.rs",
    "chars": 227,
    "preview": "#![feature(inline_const_pat)]\n\nfn f() {\n  match () {\n    y @ 0..const { 5 + 1 } => {}\n    1..=const { two() } => {}\n    "
  },
  {
    "path": "tests/output-ext/features/let_chains.f.rs",
    "chars": 1088,
    "preview": "#![feature(let_chains)]\n\nfn f() {\n  if h && let A(x) = e {\n  }\n  if a && let c = d && 1 == 1 {\n  }\n  if a && let c = d &"
  },
  {
    "path": "tests/output-ext/features/let_else.f.rs",
    "chars": 116,
    "preview": "#![feature(let_else)]\n\nlet a = 1 else { 2 };\n\n// source: \"../../../ext/jinx-rust/tests/samples/features/let_else.rs\""
  },
  {
    "path": "tests/output-ext/features/negative_impls.f.rs",
    "chars": 555,
    "preview": "#![feature(negative_impls)]\n\nimpl !Send for Test {}\nimpl !Send for Foo {}\nimpl !Sync for Foo {}\nimpl !std::marker::Unpin"
  },
  {
    "path": "tests/output-ext/features/trait_alias.f.rs",
    "chars": 145,
    "preview": "#![feature(trait_alias)]\n\ntrait A =;\ntrait A = std::fmt::Debug + Send;\n\n// source: \"../../../ext/jinx-rust/tests/samples"
  },
  {
    "path": "tests/output-ext/macro/attributes.f.rs",
    "chars": 11166,
    "preview": "// a\n\n/// b\nfn a() {}\n\nfn b() {\n  //! c\n}\n\n//////////////////////////////////\n// d\n/// let heart = '❤️';\n///////////////"
  },
  {
    "path": "tests/output-ext/macro/macro.invocation.f.rs",
    "chars": 17498,
    "preview": "declare_clippy_lint! {\n    /// ### q\n    /// ```rust\n    /// let b: Vec<&str> = vec![];\n    /// if !b.is_empty() {\n    /"
  },
  {
    "path": "tests/output-ext/macro/macro.item.f.rs",
    "chars": 388,
    "preview": "macro_rules! spaced {}\nmacro_rules! brace {\n  () => { };\n}\nmacro_rules! bracket {\n  () => { };\n}\nmacro_rules! paren {\n  "
  },
  {
    "path": "tests/output-ext/macro/macro.match.f.rs",
    "chars": 9059,
    "preview": "pub macro c {\n  () => ();\n  // _______\n  // _______\n  ($msg:expr $(,)?) => ();\n  (\n    $fmt:expr,\n    $($arg:tt)*\n  ) =>"
  },
  {
    "path": "tests/output-ext/macro/macro.tokens.f.rs",
    "chars": 10668,
    "preview": "macro_rules! a {\n  (+) => { + };\n  (+=) => { += };\n  (&) => { & };\n  (&&) => { && };\n  (&=) => { &= };\n  (@) => { @ };\n "
  },
  {
    "path": "tests/output-ext/macro/macro.transform.f.rs",
    "chars": 98380,
    "preview": "macro_rules! x {\n  () => {\n\t\t$cx.pass.$f(&$cx.context, $($args),*);\n        $(pub struct $sem;)*\n        $(pub type $nam"
  },
  {
    "path": "tests/output-ext/miscellaneous/ast-program-locs-attr-dangling.f.rs",
    "chars": 147,
    "preview": "#!shebang\n// comment\nstruct T;#[attr]\n// comment\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/ast-pro"
  },
  {
    "path": "tests/output-ext/miscellaneous/ast-program-locs-attr.f.rs",
    "chars": 139,
    "preview": "#!shebang\n// comment\n#[attr]\nstruct T;\n// comment\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/ast-pr"
  },
  {
    "path": "tests/output-ext/miscellaneous/ast-program-locs.f.rs",
    "chars": 126,
    "preview": "#!shebang\n// comment\nstruct T;\n// comment\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/ast-program-lo"
  },
  {
    "path": "tests/output-ext/miscellaneous/empty-attr-dangling-x.f.rs",
    "chars": 113,
    "preview": "#[attr]\n#[attr]\n#[attr]\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty-attr-dangling-x.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/empty-attr-dangling.f.rs",
    "chars": 95,
    "preview": "#[attr]\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty-attr-dangling.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/empty-attr-x.f.rs",
    "chars": 109,
    "preview": "#![attr]\n\n#![attr]\n\n#![attr]\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty-attr-x.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/empty-attr.f.rs",
    "chars": 87,
    "preview": "#![attr]\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty-attr.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/empty-comment-block-x.f.rs",
    "chars": 109,
    "preview": "/* */\n\n/* */\n\n/* */\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty-comment-block-x.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/empty-comment-block.f.rs",
    "chars": 93,
    "preview": "/* */\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty-comment-block.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/empty-comment-x.f.rs",
    "chars": 94,
    "preview": "//\n\n//\n\n//\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty-comment-x.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/empty-comment.f.rs",
    "chars": 84,
    "preview": "//\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty-comment.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/empty-doc-block-x.f.rs",
    "chars": 108,
    "preview": "/** */\n\n/** */\n\n/** */\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty-doc-block-x.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/empty-doc-block.f.rs",
    "chars": 90,
    "preview": "/** */\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty-doc-block.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/empty-doc-x.f.rs",
    "chars": 93,
    "preview": "///\n\n///\n\n///\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty-doc-x.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/empty-doc.f.rs",
    "chars": 81,
    "preview": "///\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty-doc.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/empty.f.rs",
    "chars": 73,
    "preview": "\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/empty.rs\""
  },
  {
    "path": "tests/output-ext/miscellaneous/shebang-b.f.rs",
    "chars": 205,
    "preview": "#!/usr/bin/env bash\n#![forbid(unsafe_code)] /* This line is ignored by bash\n# This block is ignored by rustc\n#*/\n\n//!\n\nu"
  },
  {
    "path": "tests/output-ext/miscellaneous/shebang.f.rs",
    "chars": 96,
    "preview": "#!/usr/bin/env rustx\n\n// source: \"../../../ext/jinx-rust/tests/samples/miscellaneous/shebang.rs\""
  },
  {
    "path": "tests/output-ext/patterns/pattern.f.rs",
    "chars": 3302,
    "preview": "fn a() {\n  fn eq(&&other: S) {\n    false\n  }\n  let -2147483648..=2147483647 = 1;\n  let 0..=255 = 0u8;\n  let -128..=127 ="
  },
  {
    "path": "tests/output-ext/patterns/rest.f.rs",
    "chars": 1357,
    "preview": "fn b() {\n  // fn foo(..: u8) {}\n  let ..;\n  let box ..;\n  match x {\n    .. | .. => {}\n  }\n  let &..;\n  let &mut ..;\n  le"
  },
  {
    "path": "tests/output-ext/patterns/union.f.rs",
    "chars": 5260,
    "preview": "fn fw1(H(Ok(mut x) | &Err(mut x)): H<R<'_>>) {}\nfn f1((Ok(mut x) | &Err(mut x)): R<'_>) {}\nfn fw2(H(&(Ok(x) | Err(x))): "
  },
  {
    "path": "tests/output-ext/specifiers/extern.f.rs",
    "chars": 585,
    "preview": "type funky_func = extern \"C\" fn(\n  unsafe extern \"rust-call\" fn(\n    *const JSJitInfo,\n    *mut JSContext,\n    HandleObj"
  },
  {
    "path": "tests/output-ext/specifiers/pub.f.rs",
    "chars": 1303,
    "preview": "enum E {\n  pub U,\n  pub(crate) T(u8),\n  pub(super) T {\n    f: String,\n  },\n}\npub impl Tr for S {\n  pub fn f() {}\n  pub c"
  },
  {
    "path": "tests/output-ext/statements/const.f.rs",
    "chars": 384,
    "preview": "const X: u8;\nconst B;\nconst A: u8;\npub const A: Self::AssocTy = 1;\nconst FOO: dyn Fn() -> _ = \"\";\npub const FOO: &'stati"
  },
  {
    "path": "tests/output-ext/statements/enum.f.rs",
    "chars": 1273,
    "preview": "enum E {}\n\nenum E {\n  Foo {\n    limb_with_align16: Align16,\n  },\n  Bar,\n}\nenum E {\n  Foo {\n    foo: u32,\n  },\n  Bar {\n  "
  },
  {
    "path": "tests/output-ext/statements/impl.f.rs",
    "chars": 1334,
    "preview": "impl X {}\nimpl X {\n  fn f();\n  fn f() {}\n  type Y;\n  type Z: Ord;\n  type W: Ord where Self: Eq;\n  type W where Self: Eq;"
  },
  {
    "path": "tests/output-ext/statements/self.f.rs",
    "chars": 752,
    "preview": "fn f(self) {}\nfn f(&self) {}\nfn f(mut self) {}\nfn f(&mut self) {}\nfn f(&'a self) {}\nfn f(&'a mut self) {}\nfn f(self: u8)"
  },
  {
    "path": "tests/output-ext/statements/spread.f.rs",
    "chars": 699,
    "preview": "fn main() {}\nfn f1_1(x: isize, ...) {}\nfn f1_2(...) {}\nextern \"C\" fn f2_1(x: isize, ...) {}\nextern \"C\" fn f2_2(...) {}\ne"
  },
  {
    "path": "tests/output-ext/statements/statements.f.rs",
    "chars": 3057,
    "preview": "const _: () = {\n  pub trait A {\n    const _: () = ();\n  }\n  impl A for () {\n    const _: () = ();\n  }\n  impl dyn A {\n   "
  },
  {
    "path": "tests/output-ext/statements/static.f.rs",
    "chars": 260,
    "preview": "static A: u8;\nstatic B;\n\nstatic mut C: u8;\nstatic mut D;\nstatic X: u8;\npub static B: &'static a = unsafe { &q };\nstatic "
  },
  {
    "path": "tests/output-ext/statements/struct.f.rs",
    "chars": 1601,
    "preview": "struct S<A: ?for<'a> Q, B: 'a + Q, C: 'a, G: Q + 'a, H: Q, I:>;\nstruct Empty1 {}\nstruct Empty2;\nstruct Empty7();\nstruct "
  },
  {
    "path": "tests/output-ext/statements/trait.f.rs",
    "chars": 852,
    "preview": "trait A {}\ntrait A<B: C> {}\ntrait T2<'x, 'y>: T1<'x> {}\ntrait T<E, R> = S<A> where <Self as B<D>>::T: H<R>;\ntrait A<B: C"
  },
  {
    "path": "tests/output-ext/statements/union.f.rs",
    "chars": 298,
    "preview": "union {};\nunion::b {};\nunion union<'union> {\n  union: &'union union<'union>,\n}\nstruct union;\n\nimpl union {\n  pub fn new("
  },
  {
    "path": "tests/output-ext/statements/use.f.rs",
    "chars": 3190,
    "preview": "pub use self::bb::{ aa, bb };\npub use self::cc::*;\nuse Self::f;\nuse ::super::{ S, Z };\nuse ::super::main;\nuse a::*;\nuse "
  },
  {
    "path": "tests/output-ext/types/cast.f.rs",
    "chars": 1004,
    "preview": "fn a() {\n  if (5u64 as i32 as u16) == 0u16 {\n  }\n  [\n    (u64 as u8 as i8) == 9i8,\n    &[1, 2, 3] as *const _ as *const "
  },
  {
    "path": "tests/output-ext/types/never.f.rs",
    "chars": 368,
    "preview": "fn a() {\n  a::<!>();\n  let x: ! = a!();\n  let x: ! = unsafe { a::<B, !>(C) };\n  <E as From<!>>::from(never);\n}\nfn a(x: !"
  },
  {
    "path": "tests/output-ext/types/types.f.rs",
    "chars": 13023,
    "preview": "type A where 'a: 'b + 'c = u8;\ntype A where 'a: 'b + 'c = u8;\ntype A where 'a: 'b = u8;\ntype A where 'a: 'b = u8;\ntype A"
  },
  {
    "path": "tests/print.ts",
    "chars": 637,
    "preview": "import { createPrettierPrinter, rs_print_samples } from \"../ext/jinx-rust/scripts/utils\";\nimport plugin from \"../src/ind"
  },
  {
    "path": "tests/samples/comments/assignment.rs",
    "chars": 4054,
    "preview": "f1 = |\n  //comment\n  a \n| {};\n  \nf2 = |\n  a //comment\n| {};\n  \nf3 = |\n  a\n  //comment\n| {};\n  \nf4 = // Comment\n  || {};\n"
  },
  {
    "path": "tests/samples/comments/binaryish.rs",
    "chars": 1638,
    "preview": "a = b || /** 5_______ */\nc;\n\na = b /** 6_______ */ ||\nc;\n\na = b || /** 7________________________________________________"
  },
  {
    "path": "tests/samples/comments/blocks.rs",
    "chars": 3243,
    "preview": "if 0 {\n\t0;\n\t//\n} else if 0 {\n}\n\nif 1 {\n\t/*\n\t * _______\n\t */\n}\n\nif 1 {\n  // _______\n}\n\nif 1 {\n}\n// _______\nelse {\n\n}\n\nif "
  },
  {
    "path": "tests/samples/comments/chain.rs",
    "chars": 1076,
    "preview": "_.a(a)\n/* _____________________________________________________________________________ */\n.a()\n\n_.a(\na\n)/* ____________"
  },
  {
    "path": "tests/samples/comments/closure.rs",
    "chars": 867,
    "preview": "\ncall(|/*_______*/ row| {});\nKEYPAD_NUMBERS.map(|num| ( // _______\n  1\n));\n\n\nconst obj = A {\n  f1: /* _______ */|| {},\n "
  },
  {
    "path": "tests/samples/comments/dangling.rs",
    "chars": 1749,
    "preview": "use std::{/* comment */};\n\nmacro_rules! m {/* comment */}\nmacro_rules! m { (/* comment */) /* comment */ => /* comment *"
  },
  {
    "path": "tests/samples/comments/file.rs",
    "chars": 364,
    "preview": "// This file only\n// has comments. This comment\n// should still exist\n//\n// when printed.\n\n/**\n * @typedef {DataDrivenMa"
  },
  {
    "path": "tests/samples/comments/flow.rs",
    "chars": 904,
    "preview": "loop {\n  break /* _______ */;\n  continue /* _______ */;\n}\n  \n'loop: loop {\n  break /* _______ */ 'loop;\n  break 'loop /*"
  },
  {
    "path": "tests/samples/comments/functions.rs",
    "chars": 834,
    "preview": "\nfn a(/* _______ */) {} // _______\nfn b() {} // _______\nfn c(/* _______ */ argA, argB, argC) {} // _______\nfn a(a /*____"
  },
  {
    "path": "tests/samples/comments/ignore.attr.rs",
    "chars": 237,
    "preview": "const baseline = 1 +        1;\n\nfn no() {\n  a(  a  ); #![rustfmt::skip]\n  // _______\n}\n\n#[rustfmt::skip]\nconst a    =   "
  },
  {
    "path": "tests/samples/comments/ignore.file.rs",
    "chars": 104,
    "preview": "\tconst unformatted= 1;\n\n#![rustfmt::skip] // a\n\n fn unformatted () {\n\t struct a {\n\t\t \t\t\t// b\n\t\t c\n\t }\n }"
  },
  {
    "path": "tests/samples/comments/ignore.rs",
    "chars": 1460,
    "preview": "const baseline = 1 +        1;\n\n// prettier-ignore\nlet x =\n  \"\" + this.USE + \" \" + this.STRICT + \";\\n\" +\n  this.filterPr"
  },
  {
    "path": "tests/samples/comments/imports.rs",
    "chars": 269,
    "preview": "use list::{\n    // Some item\n    SomeItem /* Comment */, /* Another item */ AnotherItem /* Another Comment */, // Last I"
  },
  {
    "path": "tests/samples/comments/macro.rs",
    "chars": 234,
    "preview": "a!(~ \" {    }  \");\na!(~ // 1\n);\na!(~ {  // 2\n});\n\ncfg_if::cfg_if! {\n    if #[attr] {\n        if 0 {\n        } else {\n   "
  },
  {
    "path": "tests/samples/comments/multiple.rs",
    "chars": 787,
    "preview": "/* _______ */ /* _______ */ /* _______ */ a;\na; /* _______ */ /* _______ */ /* _______ */\na // _______\na;\n/*1*//*2*//*3*"
  },
  {
    "path": "tests/samples/comments/parens.rs",
    "chars": 2060,
    "preview": "!x;\n!(x /* 0 */);\n!(/* 1 */ x);\n!(\n  /* 2 */\n  x\n);\n!(\n  x\n  /* 3 */\n);\n!(\n  x // 4\n);\n\n!(x + y);\n!(x + y /* 5 */);\n!(/*"
  },
  {
    "path": "tests/samples/comments/whitespace.rs",
    "chars": 904,
    "preview": "        /*\n  1\n        */\n        /*\n          2\n        */\n        /*\n\t\t\t\t\t\t\t\t  3\n        */\n        /*\n\n\n\n\n\n\n\n\n  4\n   "
  },
  {
    "path": "tests/samples/common/arrays.rs",
    "chars": 6925,
    "preview": "{for srcPath in [src, \"${src}.js\", \"${src}/index\", \"${src}/index.js\"] {}}\n{for srcPath in [123, 123_123_123, 123_123_123"
  },
  {
    "path": "tests/samples/common/assignments.rs",
    "chars": 10140,
    "preview": "const computedDescriptionLines = (showConfirm &&\n  descriptionLinesConfirming) ||\n  (focused && !loading && descriptionL"
  },
  {
    "path": "tests/samples/common/binaryish.rs",
    "chars": 10164,
    "preview": "fn f() {\n  const appEntities = getAppEntities(loadObject).filter(\n    |entity| entity && entity.isInstallAvailable() && "
  },
  {
    "path": "tests/samples/common/chains.first-argument-expansion.rs",
    "chars": 1976,
    "preview": "setTimeout(|| {\n  thing();\n}, 500);\n\n[\"a\",\"b\",\"c\"].reduce(|item, thing| {\n  return thing + \" \" + item;\n}, \"letters:\")\n\nf"
  },
  {
    "path": "tests/samples/common/chains.last-argument-expansion.rs",
    "chars": 4334,
    "preview": "crate fn searchUsers(action) {\n  return action.ofType(ActionTypes.SEARCHED_USERS)\n    .map(|| action.payload.query)\n    "
  },
  {
    "path": "tests/samples/common/chains.rs",
    "chars": 17741,
    "preview": "const thingamabobMetaAlias =\npath.scope.getProgramParent().path.get(\"body\")[0].node;\n\nfn a() {\n  fn b() {\n\t  queryThenMu"
  },
  {
    "path": "tests/samples/common/closures.rs",
    "chars": 5205,
    "preview": "fn a() { \n\tasync |x|\n\tx\n}\n\n|aaaa| {}\n\nx = |bifornCringerMoshedPerplexSawder| (|askTrovenaBeenaDependsRowans, glimseGlyph"
  },
  {
    "path": "tests/samples/common/destructuring.rs",
    "chars": 744,
    "preview": "const [one, two @ null, three @ null] = arr;\na = |[s @ 1,]| 1\nconst A { children, .. } = this.props\n\nconst A { user: A {"
  },
  {
    "path": "tests/samples/common/members.rs",
    "chars": 1979,
    "preview": "(if valid\n  { helper.responseBody(this.currentUser)}\n  else{helper.responseBody(this.defaultUser)})\n.prop;\n\nconst veryVe"
  },
  {
    "path": "tests/samples/common/types.rs",
    "chars": 8539,
    "preview": "const bar1 = [1,2,3].reduce(|| {\n  return [..carry, value];\n}, ([] as unknown) as [number]);\n\nconst bar3 = [1,2,3].reduc"
  },
  {
    "path": "tests/samples/issues/0.rs",
    "chars": 50950,
    "preview": "//! Expect 1 empty line below\n\n//! Expect 0 empty line below\n//! Expect 1 empty line below\n\n1;\n//! Expect 1 empty line b"
  },
  {
    "path": "tests/samples/issues/14.rs",
    "chars": 23,
    "preview": "[10.00, 10.0, 10., 10];"
  },
  {
    "path": "tests/samples/issues/21/fn_comment.rs",
    "chars": 22,
    "preview": "fn eof() {}\n// comment"
  },
  {
    "path": "tests/samples/issues/21/fn_fn.rs",
    "chars": 25,
    "preview": "fn eof1() {}\nfn eof2() {}"
  },
  {
    "path": "tests/samples/issues/21/fn_ln.rs",
    "chars": 12,
    "preview": "fn eof() {}\n"
  },
  {
    "path": "tests/samples/issues/21/ln_fn_ln.rs",
    "chars": 12,
    "preview": "\nfn eof(){}\n"
  },
  {
    "path": "tests/samples/issues/21/mod.rs",
    "chars": 10,
    "preview": "mod eof {}"
  },
  {
    "path": "tests/samples/issues/22.rs",
    "chars": 930,
    "preview": "fn preserve_last_semicolon() {\n\tif let Some(left) = node.borrow().left.as_ref() {\n\t\tdeque.push_back(left.clone());\n\t};\n\t"
  },
  {
    "path": "tests/samples/issues/25.rs",
    "chars": 599,
    "preview": "#[ generator(  yield(  i32  )  )]\nfn nums() {\n    yield_!(3);\n}\n\n// some extra samples to track changes\n#[\n// 0    \ngene"
  },
  {
    "path": "tests/samples/issues/nth-pass.rs",
    "chars": 530,
    "preview": "// prettier for javascript cannot format those in one pass\n\nreturn (\n\t// _______\n\t42\n  ) * 84 + 2;\n  return (\n\t// ______"
  },
  {
    "path": "tests/samples/macros/cfg_if.rs",
    "chars": 1188,
    "preview": "[\n    cfg_if! { if #[cfg(def)]{ use std; 0 } },\n\tcfg_if! { if #[cfg(def)]{ use std; 0 } else { 1 } },\n    cfg_if! { if #"
  },
  {
    "path": "tests/samples/macros/if_chain.rs",
    "chars": 417,
    "preview": "[\n\tif_chain! {\n\t\tif let Some(a) = b;\n\t\tif let Err(a) = b;\n\t\tlet (a, b) = c;\n\t\tif 1 + 2;\n\t\tthen { d=0; }\n\t},\n\tif_chain! {"
  },
  {
    "path": "tests/samples/macros/matches.rs",
    "chars": 152,
    "preview": "[\n\tmatches!(1 + 1,   Some(_)                     ),\n\tmatches!(1 + 1,   Some(_) | None if 1 + 1 == 2),\n\tmatches!(1 + 1, |"
  },
  {
    "path": "tests/samples/styling/blockify.rs",
    "chars": 187,
    "preview": "[\n\t|| 0,\n\t|| match 0 {},\n\t|| if 0 {},\n\t|| loop {},\n\t|| const {},\n\t|| async {},\n\n\t|| -> T 0,\n\t|| -> T match 0 {},\n\t|| -> "
  },
  {
    "path": "tests/samples/styling/canInlineBlockBody.rs",
    "chars": 1069,
    "preview": "[\n\t{ if 0 { 0 } },\n\t{ if 0 { 0 } else { 0 } },\n\t{ while 0 { 0 } },\n\t{ unsafe { 0 } },\n\t\n\t{ 0; if 0 { 0 } },\n\t{ 0; if 0 {"
  },
  {
    "path": "tests/samples/styling/needsParens.rs",
    "chars": 136,
    "preview": "let (A {} | a() | []) = ();\nif let (A {} | a() | []) = () {}\n\ntype A: B + C;\ntype A: impl B + C;\n\ntrait A = B + C;\ntrait"
  },
  {
    "path": "tests/samples/styling/needsSemi.rs",
    "chars": 53,
    "preview": "fn f() {\n\t#[cfg(unix)] { 0 }\n\t#[cfg(windows)] { 1 }\n}"
  },
  {
    "path": "tests/test.build.ts",
    "chars": 884,
    "preview": "import { exec } from \"node:child_process\";\nimport { inspect, promisify } from \"node:util\";\nimport prettier from \"prettie"
  },
  {
    "path": "tsconfig.base.json",
    "chars": 671,
    "preview": "{\n\t\"compilerOptions\": {\n\t\t\"target\": \"ES2020\", // upper target changes property order\n\n\t\t// module\n\t\t\"module\": \"ESNext\",\n"
  }
]

// ... and 2 more files (download for full content)

About this extraction

This page contains the full source code of the jinxdash/prettier-plugin-rust GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 202 files (764.2 KB), approximately 246.1k tokens, and a symbol index with 1506 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!