simonbs/Runestone
📝 Performant plain text editor for iOS with syntax highlighting, line numbers, invisible characters and much more.
Healthy across all four use cases
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 7w ago
- ✓7 active contributors
- ✓MIT licensed
Show 3 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Single-maintainer risk — top contributor 88% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/simonbs/runestone)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/simonbs/runestone on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: simonbs/Runestone
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/simonbs/Runestone shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- Last commit 7w ago
- 7 active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Single-maintainer risk — top contributor 88% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live simonbs/Runestone
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/simonbs/Runestone.
What it runs against: a local clone of simonbs/Runestone — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in simonbs/Runestone | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 76 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of simonbs/Runestone. If you don't
# have one yet, run these first:
#
# git clone https://github.com/simonbs/Runestone.git
# cd Runestone
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of simonbs/Runestone and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "simonbs/Runestone(\\.git)?\\b" \\
&& ok "origin remote is simonbs/Runestone" \\
|| miss "origin remote is not simonbs/Runestone (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "Sources/Runestone" \\
&& ok "Sources/Runestone" \\
|| miss "missing critical file: Sources/Runestone"
test -f "Example/Example/Application/AppDelegate.swift" \\
&& ok "Example/Example/Application/AppDelegate.swift" \\
|| miss "missing critical file: Example/Example/Application/AppDelegate.swift"
test -f "Example/Languages/Sources/RunestoneJavaScriptLanguage/TreeSitterLanguage.swift" \\
&& ok "Example/Languages/Sources/RunestoneJavaScriptLanguage/TreeSitterLanguage.swift" \\
|| miss "missing critical file: Example/Languages/Sources/RunestoneJavaScriptLanguage/TreeSitterLanguage.swift"
test -f "Example/Themes/Sources/RunestoneOneDarkTheme" \\
&& ok "Example/Themes/Sources/RunestoneOneDarkTheme" \\
|| miss "missing critical file: Example/Themes/Sources/RunestoneOneDarkTheme"
test -f ".github/workflows/build_and_test.yml" \\
&& ok ".github/workflows/build_and_test.yml" \\
|| miss "missing critical file: .github/workflows/build_and_test.yml"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 76 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~46d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/simonbs/Runestone"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Runestone is a performant plain text editor component for iOS that uses GitHub's Tree-sitter to parse code into a syntax tree, enabling real-time syntax highlighting, line numbers, invisible character visualization, and advanced code editing features. It's written entirely in Swift (662KB of it) and solves the problem of building a responsive, feature-rich code editor on iOS without sacrificing performance or customization. Standard iOS Swift Package structure: the main source code lives in Sources/Runestone/ (inferred from the README mentioning Documentation.docc and likely SwiftUI/UIKit components), the Example/ directory contains a full Xcode project demonstrating the editor in use, and GitHub Actions workflows handle testing, linting, and docs. The architecture likely separates syntax highlighting (Tree-sitter integration), rendering (UIView/CALayer), and text storage layers.
👥Who it's for
iOS app developers who need to embed a code editor (with syntax highlighting and line numbers) into their applications—think IDE-like tools, note-taking apps with code blocks, or educational coding platforms on iOS. Also relevant for developers building text editor libraries or contributing to open-source Swift UIKit/SwiftUI components.
🌱Maturity & risk
Actively maintained and production-ready. The repo has comprehensive CI/CD (build_and_test.yml, codeql.yml, swiftlint.yml, ui_tests.yml workflows), automated documentation builds, and an example app. The large codebase (662KB Swift) and multiple GitHub Actions workflows suggest a mature, well-tested project. However, as a single-maintainer library (simonbs), long breaks between updates could occur.
Low-to-moderate risk: it's a single-maintainer library (simonbs) with no visible dependency pinning details in the file list, and Tree-sitter is a large external C dependency that could introduce maintenance overhead. The iOS-only scope limits the audience. No breaking-change policy or stability guarantees are visible in the provided files, so major version updates should be reviewed carefully.
Active areas of work
The repo maintains active CI pipelines (build_and_test.yml, ui_tests.yml, swiftlint.yml, codeql.yml) and automated documentation deployment (build_documentation.yml). The presence of Dependabot configuration (.github/dependabot.yml) indicates proactive dependency updates. Without commit history in the file list, the exact current work is unclear, but the comprehensive test and documentation setup suggests ongoing refinement.
🚀Get running
Clone the repo and explore the example app:
git clone https://github.com/simonbs/Runestone.git
cd Runestone
open Example/Example.xcodeproj
Then build and run the Example target on iOS Simulator. For integration into your own project, add Runestone as a Swift Package dependency via Xcode's package manager UI or Package.swift.
Daily commands: Open Example/Example.xcodeproj in Xcode and press Cmd+R to build and run on the default iOS Simulator. Or from the command line:
xcodebuild -project Example/Example.xcodeproj -scheme Example -configuration Debug -destination 'generic/platform=iOS Simulator'
🗺️Map of the codebase
Sources/Runestone— Core library directory containing the main text editor implementation and Tree-sitter integrationExample/Example/Application/AppDelegate.swift— Application entry point demonstrating how to integrate Runestone editor into an iOS appExample/Languages/Sources/RunestoneJavaScriptLanguage/TreeSitterLanguage.swift— Reference implementation showing how to define a language grammar using Tree-sitter for syntax highlightingExample/Themes/Sources/RunestoneOneDarkTheme— Theme definition example showing how to create and configure color schemes for the editor.github/workflows/build_and_test.yml— CI/CD pipeline defining build, test, and quality checks that all contributors must understandPackage.swift— Swift Package Manager manifest defining dependencies, targets, and module structure
🧩Components & responsibilities
- Tree-sitter Parser (C parser (Tree-sitter), Swift wrapper) — Maintains incremental syntax tree as text changes; provides syntax tree queries for highlighting and code navigation
- Failure mode: Parser crashes on malformed input; out-of-memory on very large files; syntax tree becomes inconsistent with text
- TextKit Editor (UITextView, TextKit, NSTextStorage) — Core text input/output, selection, line wrapping, text measurement, caret rendering, undo/redo
- Failure mode: Text/selection state diverges from Tree-sitter tree; memory pressure from large text content; frame drops under rapid typing
- Syntax Highlighter (Tree-sitter queries (.scm files), NSAttributedString) — Applies highlighting rules (via Tree-sitter queries) to text to produce attributed strings
- Failure mode: Query failures cause no highlighting; incorrect token classification leads to wrong colors; performance degrades on deep syntax trees
- Theme Engine (Color assets (.xcassets), UIColor, Theme protocol) — Manages color palettes for syntax tokens and editor UI; resolves token type to UIColor
- Failure mode: Missing colors cause fallback to default; theme swap loses user customizations; dynamic color switching fails in dark mode transitions
- Language Package (Tree-sitter grammar files, highlights.scm, indentation rules) — Encapsulates Tree-sitter grammar (parser + queries) for a specific language
- Failure mode: Stale grammar misses new syntax; queries too greedy cause wrong highlighting; memory leak in C parser initialization
🔀Data flow
undefined→undefined— undefined
🛠️How to make changes
Add a new syntax highlighting language
- Create a new language package in Example/Languages/Sources/ with TreeSitterLanguage.swift defining the language class (
Example/Languages/Sources/RunestoneJavaScriptLanguage/TreeSitterLanguage.swift) - Define syntax highlighting rules using Tree-sitter query syntax in highlights.scm (
Example/Languages/Sources/RunestoneJavaScriptLanguage/highlights.scm) - Add language injection rules for nested syntax support in injections.scm (
Example/Languages/Sources/RunestoneJavaScriptLanguage/injections.scm) - Register the language in the editor by instantiating and assigning to the TextViewController (
Example/Example/Main/MainViewController.swift)
Create a new editor theme
- Create a new theme package directory in Example/Themes/Sources/ with Colors.xcassets (
Example/Themes/Sources/RunestoneOneDarkTheme/Colors.xcassets) - Define color assets for syntax token types (keywords, strings, comments, etc.) (
Example/Themes/Sources/RunestoneOneDarkTheme/Colors.xcassets/OneDarkForeground.colorset/Contents.json) - Create a theme struct conforming to the Runestone Theme protocol with color mappings (
Example/Example/Library/ThemeSetting.swift) - Apply the theme to the editor in MainViewController by setting the theme property (
Example/Example/Main/MainViewController.swift)
Add an editor feature (invisible characters, formatting)
- Identify the feature type and add configuration properties in Example/Library files (
Example/Example/Library/UserDefaults+Helpers.swift) - Implement the feature in the core editor engine (Sources/Runestone) (
Sources/Runestone) - Add UI controls in MainViewController to toggle the feature (
Example/Example/Main/MainViewController.swift) - Verify with UI tests in the test suite and CI pipeline (
.github/workflows/ui_tests.yml)
🔧Why these technologies
- Tree-sitter — Incremental parsing of code into syntax trees enables performant real-time syntax highlighting and code-aware features without reinventing language parsing
- TextKit (UITextView) — Native iOS framework for high-performance text layout, rendering, and editing with built-in accessibility support
- Swift Package Manager — Modular package structure allows separation of editor core, language definitions, and themes as independently versioned components
- DocC (Apple Documentation) — Native Swift documentation format enables in-IDE documentation and automated documentation website generation
⚖️Trade-offs already made
-
Tree-sitter C parser compiled into Swift Package
- Why: Provides accurate, language-agnostic parsing without reimplementing lexers/parsers for each language
- Consequence: Adds C interop complexity and increases binary size; requires careful memory management across C-Swift bridge
-
Query-based syntax highlighting (Tree-sitter queries) instead of hand-written tokenizers
- Why: Decouples highlighting rules from parser implementation, enabling community-contributed grammars and easier maintenance
- Consequence: Requires learning Tree-sitter query syntax; highlighting rules are less immediately obvious than regex patterns
-
Real-time parsing on main text edits without debouncing
- Why: Ensures syntax highlighting is always up-to-date with user input
- Consequence: Parser overhead can impact responsiveness on large files; requires careful performance optimization
-
Modular language and theme packages via SPM
- Why: Allows users to compose only needed languages/themes, reducing app size and dependencies
- Consequence: Adds complexity to package discovery and version management; users must explicitly include language packages
🚫Non-goals (don't propose these)
- Not a web-based editor (iOS only, no browser support)
- Not a full IDE (no debugger, project management, or build tools)
- Not a real-time collaborative editor (no multi-user support)
- Not a standalone application (designed as embeddable framework, requires host app integration)
🪤Traps & gotchas
No obvious environment variables or external services required in the provided files. However: (1) Tree-sitter is a C dependency—ensure your iOS build target links C/C++ libraries correctly; (2) the Example project uses local package resolution (Project.resolved in xcshareddata/swiftpm), so SPM caching might cause stale code if you switch branches—run swift package update if behavior seems off; (3) UI Tests (ui_tests.yml workflow) require simulator interaction, so Xcode version mismatches may cause flakes—keep Xcode up to date.
🏗️Architecture
💡Concepts to learn
- Tree-sitter — Runestone's core parsing engine—you must understand how Tree-sitter builds syntax trees and exposes query APIs to implement syntax highlighting and understand the limitations of grammar-based parsing
- Syntax Highlighting via AST Queries — Runestone uses Tree-sitter queries (S-expression patterns) to map syntax tree nodes to text attributes; understanding query syntax is essential for adding new language support or customizing highlight rules
- Line-Ending Detection (CR/LF/CRLF) — Runestone automatically detects line ending styles and allows manual specification; this is non-trivial in text editors and affects cross-platform compatibility—understanding how the editor normalizes line breaks prevents data corruption
- Indentation Auto-Detection (Spaces vs Tabs) — Runestone detects whether a file uses spaces or tabs for indentation automatically; implementing this heuristic correctly is crucial for preserving code style when editing multi-contributor projects
- CALayer-based Text Rendering — iOS plain text editors typically use CALayer or Metal for high-performance rendering of large files; Runestone's architecture likely uses this pattern to achieve the 'performant' claim in its description
- Invisible Character Visualization — Showing tabs, spaces, and line breaks as glyphs is a UX feature that requires custom text layout and rendering; understanding how this is implemented helps modify visual feedback without breaking performance
- Character Pair Insertion (Smart Quotes, Brackets) — Runestone auto-inserts closing pairs (quotes, brackets) when you type opening ones; this requires tracking cursor position and text state, and understanding the insertion logic is crucial for debugging or extending this feature
🔗Related repos
tree-sitter/tree-sitter— The foundational parsing engine that Runestone wraps; understanding Tree-sitter's grammar and query language is essential for adding syntax highlighting to new languagesapple/swift-syntax— Apple's native Swift syntax parsing library; an alternative to Tree-sitter for Swift-specific use cases, and often discussed in iOS editor developmentlouisdh/codepoint— A simpler text editor for iOS that predates some of Runestone's features; useful for understanding prior art and alternative approaches to iOS code editingglotcode/glot-editor— A web-based editor using Tree-sitter; relevant for understanding Tree-sitter integration patterns and syntax highlighting architecture that could inform Runestone's designhelix-editor/helix— A performant terminal editor using Tree-sitter; demonstrates production-grade Tree-sitter integration and performance optimization techniques applicable to iOS
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add unit tests for Tree-sitter parsing and syntax highlighting core
The repo uses Tree-sitter for code parsing and syntax highlighting, which is a critical feature. Looking at the workflow files (build_and_test.yml, ui_tests.yml), there's a strong testing infrastructure, but the Sources directory structure suggests the core parsing logic lacks dedicated unit tests. Adding tests for syntax tree parsing, token generation, and highlighting rules would catch regressions early and document expected behavior for different languages.
- [ ] Create Tests/RunestoneTests/SyntaxHighlighting/ directory for new test files
- [ ] Add unit tests for Tree-sitter language parser initialization and language detection logic
- [ ] Add tests for token generation and syntax highlighting rule application across supported languages (Swift, Python, JavaScript, etc.)
- [ ] Integrate new tests into .github/workflows/build_and_test.yml if not already running
Add GitHub Action workflow for performance benchmarking on large files
Runestone emphasizes being 'performant', but there's no dedicated workflow to track performance regressions. With features like line numbers, invisible characters, and syntax highlighting, performance on large files is critical. Adding a benchmark workflow would catch performance degradations before they reach users and provide data for optimization efforts.
- [ ] Create .github/workflows/performance_benchmark.yml with steps to run against increasingly large test files (1MB, 10MB, etc.)
- [ ] Create Sources/Runestone/Documentation.docc/Performance/Benchmarks.md to document baseline metrics
- [ ] Add benchmark test suite that measures rendering time, memory usage, and scrolling performance for large text files
- [ ] Configure workflow to comment on PRs with performance impact comparisons
Add SwiftUI integration tests and documentation in Example app
The Example app uses UIKit (AppDelegate.swift, SceneDelegate.swift, MainViewController.swift), but modern iOS development increasingly uses SwiftUI. Given that the library appears mature enough for adoption, adding a SwiftUI example view controller with integration tests would lower the barrier for SwiftUI users and showcase the library's flexibility. This is evidenced by the existing SwiftUIMenuButton.swift in the Example app.
- [ ] Create Example/Example/SwiftUI/ directory with SwiftUITextEditorExample.swift showing TextEditor integration
- [ ] Add unit tests in Tests/RunestoneTests/SwiftUI/ for SwiftUI view state management with the editor
- [ ] Add SwiftUI example to .github/workflows/build_example_project.yml build matrix
- [ ] Document SwiftUI integration patterns in Sources/Runestone/Documentation.docc/Tutorials/
🌿Good first issues
- Add comprehensive syntax highlighting support for a missing language (e.g., Rust, Go, TypeScript). Start by researching Tree-sitter grammar plugins for that language, then create theme/highlight rules in Sources/Runestone/. This leverages existing infrastructure and is isolated from core editor logic.
- Improve documentation: the Documentation.docc folder exists but tutorials may be incomplete. Write a step-by-step tutorial in Sources/Runestone/Documentation.docc/Tutorials/ showing how to customize colors and fonts (this feature is listed in the README but likely needs clearer examples).
- Add missing unit tests for edge cases in text storage or line-ending handling. The repo has automated testing (ui_tests.yml), but likely has gaps in unit test coverage for the automatic indentation detection and CRLF/LF/CR line ending logic mentioned in the README features.
⭐Top contributors
Click to expand
Top contributors
- @simonbs — 88 commits
- @eliperkins — 4 commits
- @finestructure — 3 commits
- @migueldeicaza — 2 commits
- @xavierLowmiller — 1 commits
📝Recent commits
Click to expand
Recent commits
592434a— Fix compilation on Xcode 26.4 (#421) (xavierLowmiller)d490289— Scroll edge effect blurs line numbers on iOS 26 (#404) (simonbs)fb7228b— Updates Xcode versions (#405) (simonbs)1fad339— Fix build with Xcode 16 (#375) (eliperkins)81e23a9— Allow TextLocation to be created (#372) (migueldeicaza)36d91bf— Add a lock around parse, to prevent concurrent access to the underlying parse tree. (#367) (migueldeicaza)9a48654— Adds StringSyntaxHighlighter (#363) (simonbs)78983bc— Setting selectedRange updates caret position (#364) (simonbs)ed2f83f— Adds default tab stops (#362) (simonbs)c09a14f— Removes lineHeightMultiplier from DefaultStringAttributes (#361) (simonbs)
🔒Security observations
The Runestone project demonstrates good security practices with CI/CD pipelines (CodeQL, SwiftLint, automated testing), dependency management via Dependabot, and proper use of standard Swift package management. The primary security concern is the inclusion of C-based Tree-sitter parser implementations, which carry inherent memory safety risks typical of C code. The project lacks explicit security disclosure documentation. Overall security posture is solid for an open-source editor library, with minor areas for improvement in documentation and C dependency management.
- Medium · Tree-sitter C Parser Dependencies —
Example/Languages/Sources/TreeSitterJavaScript/src/parser.c, Example/Languages/Sources/TreeSitterJavaScript/src/scanner.c. The project includes Tree-sitter parser implementations written in C (parser.c, scanner.c). C code is prone to memory safety issues like buffer overflows, use-after-free, and integer overflows. The parser.c file in TreeSitterJavaScript sources is a generated file that may contain complex parsing logic without memory safety guarantees. Fix: 1. Keep Tree-sitter and language parsers updated to latest versions. 2. Enable compiler safety flags (stack canaries, ASLR). 3. Consider using memory sanitizers during testing. 4. Monitor CVE databases for Tree-sitter vulnerabilities. 5. Implement input validation and size limits for parsed content. - Low · No Explicit Security Policy or Vulnerability Disclosure —
Repository root. The repository does not appear to have a SECURITY.md or vulnerability disclosure policy visible in the file structure. This makes it unclear how security issues should be reported. Fix: Create a SECURITY.md file that outlines the security vulnerability reporting process and response timeframes. Include a responsible disclosure policy. - Low · Example Project Entitlements Not Reviewed —
Example/Example/Example.entitlements. The Example project includes an entitlements file (Example.entitlements) which defines app capabilities. While this is for development purposes, the actual entitlements requested should be minimal and necessary. Fix: Review the entitlements in Example.entitlements to ensure only necessary capabilities are requested. Document why each entitlement is needed. Remove any unused entitlements. - Low · Dependency Management via Dependabot —
.github/dependabot.yml, Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved. The project uses Dependabot for dependency updates (visible in .github/dependabot.yml). While this is a security best practice, there's no visible evidence of a lock file (Package.resolved) being committed for the main package, only for the example. Fix: Ensure Package.resolved files are committed to version control for reproducible builds. Configure Dependabot to automatically create pull requests for dependency updates and enable auto-merge for patch updates after CI passes.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.