A powerful, cross-platform code editor component for SwiftUI
Syntax highlighting β’ Line numbers β’ Bracket matching β’ Find & Replace β’ And more!
Keystone powers the code editing features in Enter Space β a powerful cloud storage client for iOS and macOS that supports SFTP, WebDAV, S3, and 50+ cloud providers.
- Syntax Highlighting β Support for 15+ programming languages including Swift, Python, JavaScript, TypeScript, HTML, CSS, JSON, and more via TreeSitter
- Line Numbers β Configurable line number gutter with current line highlighting
- Bracket Matching β Automatic detection and highlighting of matching brackets, parentheses, and braces
- Character Pair Insertion β Auto-insert closing quotes, brackets, and parentheses
- Line Wrapping β Toggle between wrapped and horizontal scrolling modes
- Current Line Highlighting β Visual indicator for the line containing the cursor
- Line Ending Detection β Automatically detects LF, CRLF, or CR line endings
- Line Ending Conversion β Convert between different line ending formats
- Indentation Detection β Detects whether the file uses tabs or spaces
- Tab Key Support β Configurable tab behavior (insert tab or spaces)
- 11 Built-in Themes β Including System (adaptive), Xcode Light/Dark, GitHub Light, Solarized Light, One Light, Tomorrow, Monokai, Dracula, Nord, and Gruvbox Dark
- Configurable Font Size β Adjustable editor font size (8-32pt)
- Line Height β Adjustable line spacing multiplier (1.0x to 2.0x)
- Invisible Characters β Optional display of tabs, spaces, and line breaks
- Find & Replace β Full-featured find and replace with regex support, case sensitivity, and whole word matching
- Code Folding β Collapse and expand code regions based on syntax structure
- Undo/Redo History β Full undo/redo support
- TreeSitter Integration β Advanced syntax analysis with incremental parsing for real-time highlighting
- Comment Toggle β Toggle line/block comments with Cmd+/ (macOS) or toolbar button
- Status Bar β Shows cursor position, line count, line ending type, and indentation settings
- Settings View β Pre-built settings UI for all editor configuration options
- Symbol Keyboard (iOS) β Accessory keyboard with programming symbols and a Tab key
| Requirement | Version |
|---|---|
| iOS | 17.0+ |
| macOS | 14.0+ |
| Swift | 5.9+ |
| Xcode | 15.0+ |
Add Keystone to your project using Swift Package Manager:
- In Xcode, go to File β Add Package Dependencies...
- Enter the repository URL:
https://github.com/blaineam/KeyStone - Select your version rules and click Add Package
Or add it to your Package.swift:
dependencies: [
.package(url: "https://github.com/blaineam/KeyStone", from: "2.0.0")
]Then add "Keystone" as a dependency to your target:
.target(
name: "YourApp",
dependencies: ["Keystone"]
)import SwiftUI
import Keystone
struct ContentView: View {
@State private var code = """
func greet(name: String) {
print("Hello, \\(name)!")
}
greet(name: "World")
"""
@StateObject private var config = KeystoneConfiguration()
var body: some View {
KeystoneEditor(
text: $code,
language: .swift,
configuration: config
)
}
}import SwiftUI
import Keystone
struct EditorWithStatusBar: View {
@State private var code = "// Your code here"
@State private var cursorPosition = CursorPosition()
@StateObject private var config = KeystoneConfiguration()
var body: some View {
VStack(spacing: 0) {
KeystoneEditor(
text: $code,
language: .swift,
configuration: config,
onCursorChange: { position in
cursorPosition = position
}
)
EditorStatusBar(
cursorPosition: cursorPosition,
lineCount: code.components(separatedBy: "\n").count,
configuration: config,
hasUnsavedChanges: false
)
}
}
}KeystoneEditor includes a built-in toolbar with undo/redo, find/replace, line numbers, word wrap, invisible characters, and a settings button that opens the full settings panel.
import SwiftUI
import Keystone
struct EditorView: View {
@State private var code = "// Your code here"
@StateObject private var config = KeystoneConfiguration()
@StateObject private var findReplace = FindReplaceManager()
var body: some View {
KeystoneEditor(
text: $code,
language: .swift,
configuration: config,
findReplaceManager: findReplace
)
}
}The toolbar provides access to:
- Undo/Redo β With full undo history
- Find & Replace β Toggle the find/replace bar
- Go to Line β Jump to specific line:column
- Line Numbers β Toggle visibility
- Word Wrap β Toggle line wrapping
- Invisible Characters β Show tabs/spaces
- Settings β Opens the full settings sheet with themes, indentation, line endings, and more
The main configuration object for the editor. It's an ObservableObject that can be shared and persisted.
let config = KeystoneConfiguration()
// Appearance
config.fontSize = 14.0 // Font size in points (8-32)
config.lineHeightMultiplier = 1.4 // Line spacing multiplier (1.0-2.0)
config.showLineNumbers = true // Show/hide line number gutter
config.highlightCurrentLine = true // Highlight the current line
config.showInvisibleCharacters = false // Show tabs, spaces, line breaks
config.lineWrapping = true // Enable/disable line wrapping
// Behavior
config.autoInsertPairs = true // Auto-insert closing brackets/quotes
config.highlightMatchingBrackets = true // Highlight matching bracket pairs
config.tabKeyInsertsTab = true // Tab key inserts tab vs spaces
// Indentation
config.indentation = IndentationSettings(type: .spaces, width: 4)
// Line Endings
config.lineEnding = .lf // LF, CRLF, or CR
// Theme
config.theme = .monokai // Syntax highlighting theme| Theme | Description |
|---|---|
KeystoneTheme.system |
System-aware adaptive dark/light theme |
KeystoneTheme.xcodeLight |
Xcode Light |
KeystoneTheme.xcodeDark |
Xcode Dark |
KeystoneTheme.githubLight |
GitHub Light |
KeystoneTheme.solarizedLight |
Solarized Light |
KeystoneTheme.oneLight |
Atom One Light |
KeystoneTheme.tomorrowLight |
Tomorrow Light |
KeystoneTheme.monokai |
Classic Monokai dark theme |
KeystoneTheme.dracula |
Dracula dark theme |
KeystoneTheme.nord |
Nord dark theme |
KeystoneTheme.gruvboxDark |
Gruvbox Dark |
let customTheme = KeystoneTheme(
keyword: Color(hex: "#c678dd"),
type: Color(hex: "#e5c07b"),
string: Color(hex: "#98c379"),
comment: Color(hex: "#5c6370"),
number: Color(hex: "#d19a66"),
function: Color(hex: "#61afef"),
tag: Color(hex: "#e06c75"),
attribute: Color(hex: "#d19a66"),
operator: Color(hex: "#56b6c2"),
property: Color(hex: "#e5c07b")
)| Language | Extensions |
|---|---|
| Swift | .swift |
| Python | .py |
| JavaScript | .js, .mjs, .cjs |
| TypeScript | .ts, .tsx |
| C | .c, .h |
| C++ | .cpp, .hpp, .cc, .cxx |
| Go | .go |
| Rust | .rs |
| Ruby | .rb |
| HTML | .html, .htm |
| CSS | .css, .scss, .sass |
| JSON | .json |
| YAML | .yaml, .yml |
| Markdown | .md, .markdown |
| Shell | .sh, .bash, .zsh |
| Config | .conf, .ini |
| Plain Text | .txt |
The main editor view component with built-in toolbar and settings.
public struct KeystoneEditor: View {
public init(
text: Binding<String>,
language: KeystoneLanguage = .plainText,
configuration: KeystoneConfiguration,
findReplaceManager: FindReplaceManager,
cursorPosition: Binding<CursorPosition>? = nil,
scrollToCursor: Binding<Bool>? = nil,
showGoToLine: Binding<Bool>? = nil,
isTailFollowEnabled: Binding<Bool>? = nil,
onCursorChange: ((CursorPosition) -> Void)? = nil,
onScrollChange: ((CGFloat) -> Void)? = nil,
onTextChange: ((String) -> Void)? = nil,
onToggleTailFollow: (() -> Void)? = nil,
onConvertLineEndings: ((LineEnding) -> Void)? = nil,
onConvertIndentation: ((IndentationSettings) -> Void)? = nil
)
}The editor includes a built-in toolbar with settings button. When conversion callbacks are provided, they are called after the built-in conversion is complete (useful for marking documents as unsaved).
A status bar showing cursor position and file settings.
public struct EditorStatusBar: View {
public init(
cursorPosition: CursorPosition,
lineCount: Int,
configuration: KeystoneConfiguration,
hasUnsavedChanges: Bool = false,
onSettingsTap: (() -> Void)? = nil
)
}A pre-built settings panel for editor configuration. Platform-optimized (Form on iOS, GroupBox on macOS).
public struct EditorSettingsView: View {
public init(
configuration: KeystoneConfiguration,
isPresented: Binding<Bool>,
onConvertLineEndings: ((LineEnding) -> Void)? = nil,
onConvertIndentation: ((IndentationSettings) -> Void)? = nil
)
}Note: When using KeystoneEditor, settings are accessible via the built-in toolbar. Use EditorSettingsView directly only if you need standalone settings (e.g., in a preferences window).
A keyboard accessory with programming symbols.
public struct SymbolKeyboard: View {
public init(
indentString: String = " ",
onSymbol: @escaping (String) -> Void
)
}// Cursor position information
public struct CursorPosition {
public var line: Int // 1-based line number
public var column: Int // 1-based column number
public var offset: Int // Character offset from start
public var selectionLength: Int // Number of selected characters
}
// Line ending types
public enum LineEnding {
case lf // Unix/macOS (\n)
case crlf // Windows (\r\n)
case cr // Classic Mac (\r)
static func detect(in text: String) -> LineEnding
static func convert(_ text: String, to ending: LineEnding) -> String
}
// Indentation settings
public struct IndentationSettings {
public var type: IndentationType // .tabs or .spaces
public var width: Int // Number of spaces (1-8)
public var indentString: String // The actual indent string
static func detect(in text: String) -> IndentationSettings
}Keystone/
βββ Configuration/
β βββ KeystoneConfiguration.swift
β βββ KeystoneTheme.swift
βββ Features/
β βββ CodeFoldingManager.swift
β βββ FindReplaceManager.swift
β βββ UndoHistory.swift
βββ Platform/
β βββ PlatformTypes.swift
βββ Syntax/
β βββ KeystoneLanguage.swift
β βββ SyntaxHighlighter.swift
β βββ TreeSitterHighlighter.swift
βββ Types/
β βββ BracketMatching.swift
β βββ CursorPosition.swift
β βββ Indentation.swift
β βββ LineEnding.swift
βββ Views/
βββ KeystoneEditor.swift
βββ KeystoneTextView.swift
βββ EditorStatusBar.swift
βββ EditorSettingsView.swift
βββ SymbolKeyboard.swift
Keystone was heavily inspired by the excellent Runestone library by Simon B. StΓΈvring. Keystone builds upon Runestone's foundation with a focus on feature expansion and full cross-platform support for both iOS and macOS.
Keystone is available under the MIT License. See the LICENSE file for more information.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request