Skip to content

add clarity formatter guide #984

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
278 changes: 278 additions & 0 deletions content/docs/stacks/clarinet/clarity-formatter.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
---
title: Clarity Formatter
description: Learn how to use the Clarity formatter to standardize the style of your smart contract code.
---
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@/components/ui/accordion';

The Clarity formatter is a tool designed to automatically format your Clarity smart contract code according to a standardized style. Using a consistent style improves code readability and maintainability, making it easier for developers to understand, collaborate on, and learn from existing Clarity projects.

As Clarity is a relatively young language, establishing formatting standards helps reduce friction for both new and experienced developers, but these standards are still evolving! If you have suggestions for how we can improve the formatting rules, ping us on Discord or [add your thoughts here](https://github.com/hirosystems/clarinet/discussions/1689).

## Introduction

The Clarity formatter is integrated into two primary development tools:

1. **[Clarity VS Code Extension](https://marketplace.visualstudio.com/items?itemName=HiroSystems.clarity-lsp)**: Allows formatting directly within your editor.
2. **Clarinet CLI**: Enables formatting via the command line, including the ability to format entire projects.

## Configuration

The formatter applies an opinionated standard with the following default settings:

- **Line Length**: Lines wrap at 80 characters.
- **Indentation**: Uses 2 spaces for indentation.

These defaults are configurable to match your preferences, although the specific configuration method depends on whether you are using VS Code or Clarinet.

## Usage

### In VS Code

The [Clarity VS Code extension](https://marketplace.visualstudio.com/items?itemName=HiroSystems.clarity-lsp) provides several ways to format your code:

- **Format Document**: Formats the entire active file (Right-click -> Format Document or Shift+Option+F / Shift+Alt+F).
- **Format Selection**: Formats only the highlighted code (Right-click -> Format Selection).
- **Format On Save**: Automatically formats the file when you save it. This can be enabled in VS Code settings (`editor.formatOnSave`) and is off by default.

You can find the settings by searching "clarity-lsp" from the settings menu (Ctrl+, / Cmd+,) or using the settings.json directly.
For example, you can configure format-on-save for all clarity files within the settings.json with this entry:

```terminal
"[clarity]": {
"editor.formatOnSave": true,
},
```

### With Clarinet CLI

The Clarinet CLI allows you to format contracts from the command line using `clarinet format` (or the alias `clarinet fmt`). You can format individual files or all contracts defined in your project's `Clarinet.toml` manifest.

**Format all checkable contracts and print the result without saving:**

```terminal
$ clarinet format --dry-run
(define-map counters
principal
uint
)
(define-public (count-up)
(ok (map-set counters tx-sender (+ (get-count tx-sender) u1)))
)

(define-read-only (get-count
(who principal)
)
(default-to u0 (map-get? counters who))
)
```
**Format all checkable contracts (`--in-place`):**

```terminal
$ clarinet format --in-place
```

**Format a specific file using tabs and print the result without saving (`--dry-run`):**

```terminal
$ clarinet format -f contracts/my-contract.clar -t --dry-run
```

**Format a specific file, overwriting it (`--in-place`) with 4-space indents and 120 max line length:**
```terminal
$ clarinet format -f contracts/my-contract.clar -i 4 -l 120 --in-place
```

## Formatting Rules

Alongside these settings, the Clarity formatter introduces formatting rules for many basic Clarity constructs:

<Accordion type="single" collapsible className="w-full my-4">
<AccordionItem value="formatted-constructs">
<AccordionTrigger>View Formatted Constructs</AccordionTrigger>
<AccordionContent>
<ul className="list-disc pl-6 space-y-1 py-2">
<li><code>let</code></li>
<li><code>begin</code></li>
<li><code>match</code></li>
<li>tuples and map sugar</li>
<li><code>if</code></li>
<li><code>and</code>/<code>or</code></li>
<li>comments</li>
<li>public/read-only/private functions</li>
<li>constant/persisted-variable/ft/nft/impl-trait/use-trait</li>
<li><code>define-map</code></li>
<li><code>define-trait</code></li>
</ul>
</AccordionContent>
</AccordionItem>
</Accordion>

The following sections provide examples of how common constructs are formatted.

### Basic Indentation & Line Wrapping

Code is indented using 2 spaces per level. Long expressions exceeding the configured line length (default 80 characters) are automatically broken across multiple lines.

```clarity
;; Example of line wrapping
(try! (unwrap!
(complete-deposit-wrapper (get txid deposit) (get vout-index deposit))
(err (+ ERR_DEPOSIT_INDEX_PREFIX (+ u10 index)))
))
```

### Function Definitions

Functions (`define-public`, `define-private`, `define-read-only`) always span multiple lines. Arguments are double-indented if there is more than one, and if a single argument fits on the first line, it remains there.

The function body is single-indented, aligning with the closing parenthesis of the arguments. A trailing newline is added after each function definition.

```clarity
(define-public (my-func
(amount uint)
(sender principal)
)
(ok true)
)

(define-read-only (get-balance (who principal))
(ok u0)
)
```

### `let` Expressions

Bindings within a `let` expression are placed on separate lines, indented once. The body of the `let` expression is indented at the same level as the bindings.

```clarity
(let (
(a u1)
(b u2)
)
(body-expression)
)
```

### `begin` Blocks

`begin` blocks are always formatted across multiple lines, even for a single expression. The expressions within the block are indented once.

```clarity
(begin
(ok true)
)
```

### Boolean Operators (`and`, `or`)

If an `and` or `or` expression contains more than two conditions, each condition is placed on a new line, indented once.

```clarity
(or
true
(is-eq 1 2)
false
)
```

### `if` Statements

The `then` and `else` branches of an `if` statement are always placed on separate lines, indented once.

```clarity
(if (condition)
(expression-true)
(expression-false)
)
```

### `match` Expressions

`match` expressions are always multi-line. Each match arm (pattern and corresponding expression) is placed on a new line, with the pattern and expression aligned.

```clarity
;; Option syntax
(match opt
value (ok (handle-new-value value))
(ok 1)
)

;; Response syntax
(match (sbtc-transfer amount tx-sender (as-contract tx-sender))
success (ok id)
error (err (* error u1000)))))
)
```

### Tuples & Maps (Sugared Syntax)

Tuples defined using `(tuple ...)` are automatically converted to the sugared `{ key: value }` syntax.

```clarity
;; Input: (tuple (n1 u1))
;; Output:
{ n1: u1 }
```

Maps with multiple key-value pairs are broken across multiple lines. Each pair is indented once and followed by a comma (`,`), including the last pair.

```clarity
{
name: (buff 48),
owner: principal,
}
```

### Nested Maps

Indentation is applied consistently for nested map structures.

```clarity
{
name: {
first: "Hiro",
last: "Protagonist",
},
age: u33,
}
```

### Trait Definitions (`define-trait`)

Trait definitions follow specific layout rules for function signatures within the trait body.

```clarity
(define-trait token-trait
(
(transfer? (principal principal uint) (response uint uint))
(get-balance (principal) (response uint uint))
)
)
```

### Trailing Comments

Trailing comments (` ;; comment`) are preserved and placed after the expression on the same line. They do not count towards the maximum line length calculation.

```clarity
(get-value key) ;; Retrieves the stored value
```

## Ignoring Formatting

You can prevent the formatter from modifying specific sections of your code by adding a `;; @format-ignore` comment immediately before the block you wish to exclude.

```clarity
;; @format-ignore
(define-constant something (list
1 2 3 ;; comment
4 5 ))
```

Refer to the [Clarity formatter readme](https://github.com/hirosystems/clarity-formatter/blob/main/README.md) for the full list of commands and options.

## Feedback & Further Information

The Clarity formatter is continually evolving based on community feedback. Please try it out and share your thoughts to help improve the standards for the ecosystem.

For more detailed information, please consult the [Clarity formatter readme](https://github.com/hirosystems/clarity-formatter/blob/main/README.md).
1 change: 1 addition & 0 deletions content/docs/stacks/clarinet/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"index",
"quickstart",
"concepts",
"clarity-formatter",
"---Guides---",
"...guides"
]
Expand Down