The Problem

Developers need a fast, feature‑rich plain‑text editor for iOS that can handle code (syntax highlighting, line numbers, invisible‑character rendering, etc.) without pulling in heavyweight UI frameworks. Existing options are either heavy, lack Tree‑sitter‑based incremental parsing, or are tied to macOS only.

What This Does

Runestone delivers a Swift‑native editor component built on Tree‑sitter for incremental parsing. The core UI lives in Sources/Runestone/TextView/Core/, e.g. TextView.swift (the primary UIView subclass) and TextInputView.swift (handles input, cursor, and selection). Syntax‑highlighting rules are defined in language packages under Example/Languages/Sources/, such as RunestoneJavaScriptLanguage/TreeSitterLanguage.swift. Themes are pure‑Swift assets in Example/Themes/Sources/, e.g. RunestoneOneDarkTheme/OneDarkTheme.swift. The sample iOS app (Example/Example.xcodeproj) wires the editor in Example/Example/Application/SceneDelegate.swift and demonstrates usage of helpers like TextView+Helpers.swift.

How It Is Wired

Entry point – the iOS launch sequence starts in Example/Example/Application/SceneDelegate.swift, which creates a UIWindow and sets MainViewController as root. MainViewController imports Runestone and instantiates Runestone.TextView (implemented in Sources/Runestone/TextView/Core/TextView.swift).

Parsing – TextView holds a TreeSitterLanguage instance (see Example/Languages/Sources/RunestoneJavaScriptLanguage/TreeSitterLanguage.swift). On text changes, TextEditHelper.swift (in Sources/Runestone/Library/) batches edits and forwards them to the language’s tree_sitter parser (C source Example/Languages/Sources/TreeSitterJavaScript/src/parser.c). The parser returns a syntax tree that drives the RedBlackTree‑based line model (Sources/Runestone/RedBlackTree/RedBlackTree.swift).

Rendering – TextView walks the syntax tree, applying HighlightName definitions from theme files (RunestoneThemeCommon/HighlightName.swift) to generate NSAttributedString fragments. The view also consults TextView+Helpers.swift for invisible‑character rendering and line‑number drawing.

Effect scope – The editor component does not perform I/O itself; all side effects (file saving, network sync) are expected to be handled by the host app. The widest blast radius within the repo is TextView.swift (≈1,500 LOC) and TextInputView.swift, because they own the view hierarchy, edit batching, and interaction with the parser.

No circular import edges were detected; the internal module graph is flat, simplifying future refactoring.

How To Use It

# clone the repo
git clone https://github.com/moses-y/Runestone
cd Runestone

# build the example app (SwiftPM)
swift build -c release

# open the Xcode project to run on a simulator/device
open Example/Example.xcodeproj

Add the package to another project by pointing Xcode to Package.swift. Import Runestone and create a Runestone.TextView in a view controller. Configure a language by adding a TreeSitterLanguage subclass (see RunestoneJavaScriptLanguage/TreeSitterLanguage.swift) and a theme by conforming to EditorTheme (see OneDarkTheme.swift).

Real‑World Use

A note‑taking app can embed Runestone.TextView to provide code‑aware editing. The host app registers TreeSitterLanguage for each supported language, then persists the editor’s text property to iCloud. Because parsing is incremental, scrolling through a 10 k‑line source file remains smooth on iPhone 12.

Code Health & Issues

Static analysis findings (48 total, 20 high / 28 medium):

  • Deep nesting – up to 8 levels in Example/Languages/Sources/RunestoneJavaScriptLanguage/TreeSitterLanguage.swift, Example/Languages/Sources/TreeSitterJavaScript/src/scanner.c, Sources/Runestone/Library/TextEditHelper.swift.
  • Duplicated code blocks – 528‑line fragments repeated across 77 files (e.g., SceneDelegate.swift in both Example and UITests).
  • Oversized files – Sources/Runestone/TextView/Core/TextView.swift, Sources/Runestone/TextView/Core/TextInputView.swift, Sources/TestTreeSitterLanguages/html/parser.c each exceed 1,400 lines.
  • High branching density – 38 branches in 85 lines in Sources/Runestone/RedBlackTree/RedBlackTree.swift and similar files.

CI/CD hygiene (5 audit findings):

  • HIGH – Pin third‑party actions to commit SHAs in .github/workflows/*.
  • HIGH – Remove continue-on-error from correctness steps in build_and_test.yml.
  • MEDIUM – Declare least‑privilege permissions for GITHUB_TOKEN.
  • MEDIUM – Move large binary Design/Tutorials.sketch (28 MB) to Git LFS.
  • LOW – Add timeout-minutes to workflow jobs.

All tests (89 files) and documentation (9 doc files) are present, and a license is included.

The Bottom Line

Runestone provides a solid, Tree‑sitter‑backed iOS text editor with rich code‑editing features, but the core UI files are large and contain deep nesting, making future maintenance non‑trivial. It is well‑suited for teams that need a performant in‑app code editor and are comfortable refactoring the identified hotspots.