<!-- cSpell: ignore gitk -->
# Slint development guide

The build instructions are in the [building.md](./building.md) file.
The testing instructions are in the [testing.md](./testing.md) file.

## Environment Setup

[`mise-en-place`](https://mise.jdx.dev/) can be used to install the necessary
development tooling. After installing `mise` and registering `mise` with your
shell, go into your git checkout directory and `mise trust -a` the configuration
we ship. Afterwards `mise install` makes all the necessary tooling available.

Even if you do not want to use mise: `.mise/config.toml` contains a handy list of tools
to make available.

If you would like to automatically invoke autofix tasks (formatting, linting) before committing changes using a git pre-commit hook, set up the hook with the following command:

```
mise generate git-pre-commit --write --task=ci:autofix:fix
```

## Repository structures

For the crate/directory map of `internal/`, `api/`, `tools/`, and `editors/`, see the
Architecture section of [AGENTS.md](../AGENTS.md) — kept there as the single source of truth
so this file doesn't drift out of sync with it. `internal/compiler/tests/` holds the syntax
tests, which test that the compiler reports the right error for invalid input.

This section covers the rest of the repository layout that AGENTS.md doesn't:

### `helper_crates`

A set of crates that are somehow not strictly related to Slint, and that could be moved to
their own repository and have their own version release at some point.

### `tests`

The integration tests that exercise a set of `.slint` files across the different front-ends
(Rust, C++, interpreter, Node.js, Python). See [testing.md](./testing.md). This is its own
Cargo workspace, as are `examples/`, `demos/`, and `ui-libraries/material/` (see
[building.md](./building.md#workspace-layout)).

### `examples` and `demos`

Small, manually-run examples (`examples/`) and full demo applications (`demos/`), showing how
to use the Slint markup language and interact with a UI from each supported host language.

### `ui-libraries`

Widget libraries shipped separately from the compiler's built-in widgets, currently
`material` (a Material Design implementation and its gallery).

### `xtask`

Repository maintenance tasks run via `cargo xtask <task>`: license-header checks, REUSE
compliance, generating the cbindgen headers the C++ docs need, and Node package prep.
Run `cargo xtask --help` for the full list.

### `ai-plugins`

The Slint skill and marketplace manifests shipped to AI coding assistants (Claude Code,
Cursor); see [ai-plugins/skills/slint/SKILL.md](../ai-plugins/skills/slint/SKILL.md).

## Documentation

There are some documentation comments in the code.
HTML documentation can be generated with something like

```sh
cargo doc --document-private-items --no-deps --open
```

The documentation that lives on <https://docs.slint.dev> is rendered with Astro Starlight.
See the ./astro/README.md file for details.

For a deep dive into certain aspects of slint's architecture and implementation, see the files in the **`development`** subdirectory.

## Rust to C++ bindings

We use a rather complex mechanism to expose internal data structures implemented in Rust to C++, in a way that allows us to provide a nice C++ API.

As a starting point, we recommend reading the blog entry we published about this:

[https://slint.dev/blog/expose-rust-library-to-other-languages.html](https://slint.dev/blog/expose-rust-library-to-other-languages.html)

What this article omits are how we invoke cbindgen and what kind of tweaks we apply on various levels:

The C++ library consists of four components:

1. The `slint-cpp` cdylib created by `cargo`/`rustc` from `api/cpp`.
2. The public header files in `api/cpp/include`.
3. Internal header files generated by `cbindgen` as part of the `slint-cpp` build process.
4. The CMake project to tie it all together by invoking `corrosion` to call `cargo`.

### `cbindgen`

The `cbindgen` invocation generates multiple header files for four different modules:

1. The types in the core library. This is the bulk of the generated code.
2. The entry points into the C++ library for creating backends, invoking the event loop, etc. - from `api/cpp/lib.rs`.
3. The types specific to the Qt backend used by the Qt style, such as `NativeButton`, etc.
4. The types used by the C++ interpreter API, written to `slint_interpreter_internal.h`.

Typically the input to `cbindgen` is within `ffi` sub-modules in the corresponding input crates to `cbindgen`. These `ffi` modules are gated with `#[cfg(feature = "ffi")]`.

## Commit History & Code Reviews

Linear history is preferred over merge commits. Long lived features can live in feature branches and those can be integrated
with merge commits, of course.

As a consequence, we typically integrate pull requests as "rebase and merge" or "squash and merge".

During code review, consider adding small commits on top to make it easier for the reviewer and contributor to track feedback and
how the feedback was incorporated - rebase can tend to make it harder. It's perfectly fine to then squash these commits when the
review phase is complete and approval is given.

Example:

A PR consists of three commits:

1. Add new widget
2. Add documentation for new widget
3. Change example to use new widget

In the review phase, the reviewer suggests to make changes to the widget implementation and the documentation. Consider pushing
these as follow-up fixes:

1. Add new widget
2. Add documentation for new widget
3. Change example to use new widget
4. Fix an issue in widget found during review
5. Fix a typo in the documentation

Finally, the PR is approved. As contributor, in your local branch, feel free to merge 4. into 1. and 5. into 2.:

(commits are real, sha1s are just examples)

As a first step, let's rebase our changes to make sure that there are no conflicts:

```bash
git rebase origin/master
```

This might run through without stopping. If there are merge conflicts to be resolved, `git rebase` will stop
and let you fix it. For instructions how to resolve the conflicts and continue, see [Resolving merge conflicts after a Git rebase](https://docs.github.com/en/get-started/using-git/resolving-merge-conflicts-after-a-git-rebase).

When your branch is rebased, proceed to squash the fixup commits. Start an interactive rebase that starts at the base commit:

```
$ git rebase -i origin/master
```

This launches the configured editor with the above list of commits,
in the order as they will committed:

```
pick 82916bc2 Add new widget
pick e55bde4c Add documentation for new widget
pick 9bc8d203 Change example to use new widget
pick a6feda52 Fix an issue in widget found during review
pick 032032dc Fix a typo in the documentation
```

Let's merge 4. into 1. and 5. into 2. by changing the above:

```
pick 82916bc2 Add new widget
fixup a6feda52 Fix an issue in widget found during review
pick e55bde4c Add documentation for new widget
fixup 032032dc Fix a typo in the documentation
pick 9bc8d203 Change example to use new widget
```

Save and exit the editor.

Now git will start at the base commit, cherry-pick the first commit, and squash the "Fix an issue in widget found during code review"
change into the same commit. Use `squash` instead of `fixup` if you want to also further edit the commit message.

Save and exit the editor.

Now git continues to do the same with the second squash. Do as in the previous step and adjust the commit message to suit the
original intent ("Add documentation for new widget").

Save and exit the editor. Rinse and repeat until the rebase is complete.

Use a tool like [GitHub Desktop](https://desktop.github.com) or [gitk](https://git-scm.com/docs/gitk) to take another look at the
git history of commits.

Are all the fix-up commits merged with the original changes? Do the commit messages look okay?

If you need further changes, run `$ git rebase -i origin/master` again.

When the history is clean, double check that you're going to push to the right branch:

```
$ git push --dry-run -f
```

If that looks okay and targets the right branch for your PR, push with force:

```
$ git push -f
```
