# simonbs/Runestone: a Tree-sitter backed plain text editor framework for iOS

> Runestone is a Swift framework, not an app, that gives iOS editors syntax highlighting, line numbers and invisible character rendering on top of Tree-sitter. It is MIT licensed, and its last push was on 2026-03-25.

**simonbs/Runestone** — 📝 Performant plain text editor for iOS with syntax highlighting, line numbers, invisible characters and much more.

- Repository: https://github.com/simonbs/Runestone
- Stars: 3,222 · Forks: 224
- Language: Swift
- License: MIT
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/simonbs-runestone

## The gap Runestone fills between UITextView and a real code editor

UIKit ships UITextView, which edits text and does nothing else. If the text happens to be Swift, JSON or Python, the user gets a wall of monochrome characters: no line numbers, no indication that a tab is a tab and not four spaces, no way to see where a line ends when wrapping is on. Building those features on top of UITextView means writing a line index that stays correct through every edit, a tokenizer, and a rendering layer that keeps up while the user types. That is weeks of work before the first useful screen.

Runestone is aimed at iOS developers who need that layer and do not want to write it. The README describes it as a performant plain text editor for iOS with code editing features, and the feature list is concrete: syntax highlighting, line numbers, highlighting of the selected line, rendering of invisible characters (tabs, spaces and line breaks), insertion of character pairs such as the trailing quotation mark, colour and font customisation, a line wrap toggle, adjustable line height, a page guide, vertical and horizontal overscroll, range highlighting, regular expression search, automatic detection of spaces versus tabs for indentation, and explicit control over CR, LF or CRLF line endings when inserting a break. That is an editor surface, not a text field.

It is not a general purpose text component. It is a framework you link into an app you are already building, and the README points at a sibling app of the same name on the App Store as the reference consumer of every feature. If you are not shipping an iOS app, nothing here applies to you.

## How Tree-sitter parsing and the line tree fit together

Two mechanisms carry the design. The first is Tree-sitter, which the README credits for parsing code into a syntax tree, and the second is AvalonEdit's approach to managing lines in a document, which the README says was translated to Swift.

The Tree-sitter half is what makes highlighting survive typing. A conventional highlighter re-tokenizes the whole buffer on every keystroke, which is fine for a settings file and painful for a long source file. Tree-sitter parses incrementally, so an edit invalidates only the affected region of the tree and the rest is reused. The README states that Runestone's performance is by far mostly thanks to this incremental parsing plus the AvalonEdit line management. The syntax tree is also the substrate for anything that needs to understand the code rather than merely colour it, which is why the feature list can include things beyond highlighting without a second parser.

The AvalonEdit half is about coordinates. An editor constantly converts between a flat character offset and a line and column, and it does so while text is being inserted and deleted. AvalonEdit's DocumentLineTree keeps that mapping in a balanced structure so lookups do not degrade as the document grows. Runestone's README is explicit that this part is a translation from C# into Swift, not an original design, and that the project acknowledges it. That is a reasonable engineering choice, and it also tells you where the interesting bugs will live: ported tree code is where off-by-one errors hide.

The README adds a performance caveat that is easy to skip past. It says that when judging performance, it is key to build your app in the release configuration, because the compiler optimisations become very apparent when opening large documents. A debug build of an app embedding Runestone is not a fair sample of how it behaves.

## Installing Runestone and getting a highlighted view on screen

The README does not print a package URL or a version pin. It points to a Getting Started article at docs.runestone.app and a tutorial series called Meet Runestone, and the repository root contains a Package.swift and a Package.resolved, which is what you would expect of a Swift Package Manager library. Treat the documentation site as the source of truth for the current integration steps rather than copying a snippet from a blog post.

What the README does document precisely is the clone, and it matters because Runestone depends on Tree-sitter through a submodule. Cloning without the submodule leaves you with a project that will not build.

```bash
git clone --recursive git@github.com:simonbs/Runestone.git
```

The README states that the --recursive option is required so that all submodules are cloned. If you already cloned without it, the .gitmodules file at the repository root lists what is missing and you would fetch those separately.

Once it builds, the first real use is a text view holding source code. The repository ships an Example project under Example/Example.xcodeproj with Example/Languages and Example/Themes directories, which is the fastest way to see a configured editor rather than reading about one. Open that project, run it on a device or simulator, and you get a working editor with grammars and colour themes already wired up. The framework's own public API is documented at docs.runestone.app, generated with Apple's DocC compiler, so the type names and initialiser signatures you need are there rather than in the README. Read the Getting Started article before you invent your own configuration path; the Example project's setup is the pattern the author maintains.

## Where Runestone stops: Catalyst, grammars and the release-build caveat

The clearest limitation is stated by the project itself. The README says the project should mostly work with Catalyst on the Mac, but that it is not fully tested and the implementation is not considered done, and that the focus is currently on the iPhone and iPad. If your product is a Mac app first, Runestone is not the component you want, and the README is not hedging about it.

The second constraint is grammars. Tree-sitter highlighting only works for languages you have a grammar for, and the README does not enumerate which grammars the framework ships or how you add one. The Example project has a Languages directory, which suggests grammars are supplied per app rather than being magically available. Budget time for that: a grammar is a build artefact with its own version, and it can drift from the framework. Nothing in the README describes a compatibility policy between Runestone releases and grammar versions.

The third is the performance configuration already mentioned. If you profile a debug build, you will conclude the framework is slow when the README says the opposite is true in release. That is a trap for anyone evaluating it casually.

Finally, consider the shape of the project. It is a framework maintained alongside a commercial app of the same name, and the README frames contributions as welcome when they fit the author's vision. That is a normal arrangement for an editor component, but it means roadmap decisions are made by one person's product needs. The last push was on 2026-03-25, and the release history shows 0.5.0 in March 2024, 0.5.1 in June 2024, and 0.5.2 in March 2026, so the cadence is irregular rather than steady. Plan for the possibility of sitting on a version for a long stretch.

## Runestone against building on UITextView or porting a web editor

The realistic alternative is not another iOS framework. It is doing the work yourself on top of UITextView, or embedding a JavaScript editor such as CodeMirror or Monaco in a WKWebView.

The UITextView route gives you total control and zero dependency risk. You also inherit every problem: you write the line index, you pick a tokenizer, you handle the case where a user pastes 200 KB of minified JSON, and you maintain all of it. Runestone's answer to that is a translated AvalonEdit line tree and Tree-sitter's incremental parser, which is a specific, documented design rather than a vague promise of speed. If your editor only ever shows short snippets, that machinery is more than you need and the dependency is not worth it.

The web view route gets you a mature editor with a large grammar ecosystem and a rendering model built for text. The difference is architectural: you are running a JavaScript engine inside your app, bridging selection and content between two runtimes, and accepting the memory and startup cost of that. Runestone stays in Swift and renders with the platform's own text stack, which is why features like invisible character rendering and native selection behave the way iOS users expect. The trade is that you are limited to what the framework exposes and to grammars that exist for Tree-sitter.

If you need a server-side or cross-platform editor, none of these three is the right frame, and Runestone in particular is iOS only.

## Licence, maintenance and what upgrading costs

Runestone is MIT licensed, and the LICENSE file sits at the repository root. MIT is permissive: you can use the framework in a closed source app, and the obligation is to keep the copyright and permission notice with the distribution. Tree-sitter, which Runestone depends on through a submodule, is a separate project with its own licence, and the grammars you add are separate again. Check each one rather than assuming the MIT label covers the whole dependency graph. This is not legal advice; if the distinction matters to your release, have someone qualified read the notices.

Upgrade cost has two parts. The framework itself is a Swift package, so moving between versions is a package resolution change, but the release history shows gaps of many months between 0.5.0, 0.5.1 and 0.5.2, which means fixes may not arrive on your schedule. The bigger cost is the Tree-sitter submodule and your grammars. A submodule is pinned by commit, so a framework update can require a submodule update, and a grammar built against an older Tree-sitter version may need rebuilding. Nothing in the README describes a supported combination matrix, so the practical approach is to pin both the package version and the submodule commit in your own repository and upgrade them together, deliberately.

There is no stated deprecation policy and no migration guide referenced in the README. The documentation site is generated from the Swift source with DocC, so it tracks the code rather than a hand-maintained changelog.

## Conclusion

Adopt Runestone if you are building an iPhone or iPad app that needs a code-aware text view and you are willing to vendor Tree-sitter grammars and test in release configuration. Do not adopt it if you target macOS as a first-class platform, since the README says Catalyst is not fully tested and the implementation is not considered done, or if you need an editor you can drop into a non-Apple stack. Before writing code, clone with --recursive so the Tree-sitter submodule is present, then decide which grammars your app ships and how you will keep them in step with the framework.

## FAQ

### What is the Runestone app and how does it relate to the framework?

The README says the Runestone framework is used by an app of the same name, a plain text editor for iPhone and iPad that uses all the features of the framework. The app is distributed on the App Store, and the framework is the library you link into your own app.

### What is the purpose of simonbs/Runestone?

It is a plain text editor framework for iOS with code editing features, built so that an app can show syntax highlighting, line numbers and invisible characters without writing its own tokenizer and line index. Tree-sitter parses the code into a syntax tree that the framework uses for features that require understanding the code.

### Does Runestone work on macOS through Catalyst?

Partly. The README states that the project should mostly work with Catalyst on the Mac, but that it is not fully tested and the implementation is not considered done, with the focus currently on the iPhone and iPad.

### Why does Runestone need to be cloned with --recursive?

The README states that Runestone depends on Tree-sitter through a submodule, and that the submodule must be cloned as well before Runestone can be built. Passing --recursive when cloning the repository clones all submodules.

### Why does Runestone feel slow when I build my app in debug?

The README says that when judging the performance of Runestone it is key to build your app in the release configuration, because the compiler optimisations become very apparent when opening large documents. A debug build is not a fair measurement.

## Sources

- [Issues](https://github.com/simonbs/Runestone/issues)
- [License: MIT](https://github.com/simonbs/Runestone/blob/main/LICENSE)
- [README](https://github.com/simonbs/Runestone/blob/main/README.md)
- [Releases](https://github.com/simonbs/Runestone/releases)
- [simonbs/Runestone on GitHub](https://github.com/simonbs/Runestone)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/simonbs-runestone
