### Create and Configure TextView Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/GettingStarted.md Initialize a TextView and configure its appearance and behavior, such as line numbers, page guides, and line height. ```swift let textView = TextView() view.addSubView(textView) ``` ```swift // Show line numbers. textView.showLineNumbers = true // Highlight the selected line. textView.lineSelectionDisplayType = .line // Show a page guide after the 80th character. textView.showPageGuide = true textView.pageGuideColumn = 80 // Show all invisible characters. textView.showTabs = true textView.showSpaces = true textView.showLineBreaks = true textView.showSoftLineBreaks = true // Set the line-height to 130% textView.lineHeightMultiplier = 1.3 ``` -------------------------------- ### Implement TextViewDelegate for Editor Callbacks Source: https://context7.com/simonbs/runestone/llms.txt Implement the TextViewDelegate protocol to respond to various editor events. This example shows how to gate editing, validate keystrokes, sync the document model, update the status bar, provide feedback for looping search, and handle highlighted range replacements. ```swift import Runestone extension MyViewController: TextViewDelegate { // Gate editing (e.g., read-only mode) func textViewShouldBeginEditing(_ textView: TextView) -> Bool { !isReadOnly } // Validate every keystroke func textView(_ textView: TextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { guard text != "\n" else { return true } return text.unicodeScalars.allSatisfy { CharacterSet.alphanumerics.union(.whitespaces).contains($0) } } // Sync document model func textViewDidChange(_ textView: TextView) { document.text = textView.text document.markDirty() } // Update status bar func textViewDidChangeSelection(_ textView: TextView) { let range = textView.selectedRange statusBar.update(range: range) } // Looping search navigation feedback func textViewDidLoopToFirstHighlightedRange(_ textView: TextView) { showToast("Reached last match, looping to first") } // Allow highlighted-range replacement (e.g., find-replace toolbar) func textView(_ textView: TextView, canReplaceTextIn highlightedRange: HighlightedRange) -> Bool { true } func textView(_ textView: TextView, replaceTextIn highlightedRange: HighlightedRange) { textView.replace(highlightedRange.range, withText: replacementField.text ?? "") } } ``` -------------------------------- ### Implement Custom Theme for Runestone Editor Source: https://context7.com/simonbs/runestone/llms.txt Implement the `Theme` protocol to control all colors and fonts in the editor, including syntax highlighting token colors. This example shows a `OneDarkTheme` implementation. ```swift import Runestone import UIKit final class OneDarkTheme: Theme { // Base typography var font: UIFont { .monospacedSystemFont(ofSize: 14, weight: .regular) } var textColor: UIColor { UIColor(red: 0.78, green: 0.79, blue: 0.81, alpha: 1) } // Gutter var gutterBackgroundColor: UIColor { UIColor(white: 0.15, alpha: 1) } var gutterHairlineColor: UIColor { UIColor(white: 0.2, alpha: 1) } var lineNumberColor: UIColor { UIColor(white: 0.4, alpha: 1) } var lineNumberFont: UIFont { .monospacedSystemFont(ofSize: 12, weight: .regular) } // Selected line var selectedLineBackgroundColor: UIColor { UIColor(white: 0.18, alpha: 1) } var selectedLinesLineNumberColor: UIColor { .white } var selectedLinesGutterBackgroundColor: UIColor { UIColor(white: 0.18, alpha: 1) } // Invisible characters var invisibleCharactersColor: UIColor { UIColor(white: 0.35, alpha: 1) } // Page guide var pageGuideHairlineColor: UIColor { UIColor(white: 0.3, alpha: 1) } var pageGuideBackgroundColor: UIColor { UIColor(white: 0.17, alpha: 1) } // Marked text (CJK input) var markedTextBackgroundColor: UIColor { UIColor(white: 0.3, alpha: 1) } var markedTextBackgroundCornerRadius: CGFloat { 3 } // Token colors — called per Tree-sitter capture name func textColor(for highlightName: String) -> UIColor? { switch highlightName { case "keyword": return UIColor(red: 0.82, green: 0.52, blue: 0.76, alpha: 1) case "string": return UIColor(red: 0.56, green: 0.74, blue: 0.49, alpha: 1) case "comment": return UIColor(white: 0.48, alpha: 1) case "function": return UIColor(red: 0.38, green: 0.67, blue: 0.94, alpha: 1) case "number": return UIColor(red: 0.90, green: 0.72, blue: 0.51, alpha: 1) case "type": return UIColor(red: 0.90, green: 0.72, blue: 0.51, alpha: 1) case "variable.builtin": return UIColor(red: 0.90, green: 0.50, blue: 0.45, alpha: 1) default: return nil // fall back to textColor } } func fontTraits(for highlightName: String) -> FontTraits { switch highlightName { case "keyword": return .bold case "comment": return .italic default: return [] } } } ``` -------------------------------- ### Create StringSyntaxHighlighter Instance Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/SyntaxHighlightingAString.md Instantiate StringSyntaxHighlighter with a theme and a language. Ensure you have followed guides on adding Tree-sitter languages and creating themes. ```swift let syntaxHighlighter = StringSyntaxHighlighter( theme: TomorrowTheme(), language: .javaScript ) ``` -------------------------------- ### Clone Runestone Repository with Submodules Source: https://github.com/simonbs/runestone/blob/main/README.md Use this command to clone the Runestone repository and its Tree-sitter submodule. Ensure you have Git installed. ```bash git clone --recursive git@github.com:simonbs/Runestone.git ``` -------------------------------- ### Register Tree-sitter Grammar for a Language Source: https://context7.com/simonbs/runestone/llms.txt Wrap a compiled Tree-sitter grammar with optional highlights query, injections query, and indentation scopes. This example demonstrates creating a JavaScript language configuration. ```swift import Runestone import TreeSitter // tree_sitter_javascript() is the C function exported by the grammar library. func makeJavaScriptLanguage() -> TreeSitterLanguage { // Load the highlights query from a bundled .scm file let highlightsURL = Bundle.main.url(forResource: "highlights", withExtension: "scm", subdirectory: "JavaScript")! let injectionsURL = Bundle.main.url(forResource: "injections", withExtension: "scm", subdirectory: "JavaScript")! let indentScopes = TreeSitterIndentationScopes( indent: ["statement_block", "switch_body", "class_body"], inheritIndent: [], outdent: ["}"], whitespaceDenotesBlocks: false ) let language = TreeSitterLanguage( tree_sitter_javascript(), highlightsQuery: .init(contentsOf: highlightsURL), injectionsQuery: .init(contentsOf: injectionsURL), indentationScopes: indentScopes ) // Optionally pre-warm on a background thread before first use DispatchQueue.global(qos: .userInitiated).async { language.prepare() } return language } ``` -------------------------------- ### Page Guide Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Controls the visibility and column of the page guide. ```APIDOC ## Page Guide ### Page Guide - ``showPageGuide`` - ``pageGuideColumn`` ``` -------------------------------- ### Dynamically Switch Language Mode in TextView Source: https://context7.com/simonbs/runestone/llms.txt Change the active language of an already-displayed `TextView` at runtime without recreating the view. This example shows switching to Python. ```swift import Runestone // Switch from plain text to Python after user opens a .py file func switchToPython(textView: TextView) { let pythonLanguage = makePythonLanguage() // returns TreeSitterLanguage let mode = TreeSitterLanguageMode(language: pythonLanguage) textView.setLanguageMode(mode) { success in if success { print("Re-parsed as Python, syntax highlighting active") } else { print("Parse failed — language not set") } } } ``` -------------------------------- ### Initializing StringSyntaxHighlighter Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/StringSyntaxHighlighter.md Create a syntax highlighter by providing a theme and a language. You can also optionally provide a custom language provider. ```APIDOC ## ``StringSyntaxHighlighter/init(theme:language:languageProvider:)`` ### Description Initializes a new instance of ``StringSyntaxHighlighter`` with the specified theme, language, and an optional language provider. ### Parameters - **theme** (`Theme`): The theme to use for syntax highlighting. - **language** (`SyntaxLanguage`): The programming language of the code to be highlighted. - **languageProvider** (`SyntaxLanguageProvider?`): An optional custom provider for syntax highlighting rules. ``` -------------------------------- ### Initialization Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Initializes the TextView with specified frame or from a coder. ```APIDOC ## Initialization ### Initializing the Text View - ``init(frame:)`` - ``init(coder:)`` ``` -------------------------------- ### Initialize Tree-sitter Language with TreeSitterLanguages Package Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/AddingATreeSitterLanguage.md Create a TextViewState using a predefined Tree-sitter language from the TreeSitterLanguages package. This is the recommended approach when using the package. ```swift let text = "let foo = \"Hello World\"" let state = TextViewState(text: text, language: .javaScript) textView.setState(state) ``` -------------------------------- ### Create and Use StringSyntaxHighlighter Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/StringSyntaxHighlighter.md Instantiate StringSyntaxHighlighter with a theme and language, then use it to highlight a code string. ```swift let syntaxHighlighter = StringSyntaxHighlighter( theme: TomorrowTheme(), language: .javaScript ) let attributedString = syntaxHighlighter.syntaxHighlight( """ function fibonacci(num) { if (num <= 1) { return 1 } return fibonacci(num - 1) + fibonacci(num - 2) } """ ) ``` -------------------------------- ### Configure TextView for Editor Functionality Source: https://context7.com/simonbs/runestone/llms.txt Set up the `TextView` component by configuring its appearance, behavior, and delegate for text editing. ```swift import Runestone import UIKit class EditorViewController: UIViewController { private let textView = TextView() override func viewDidLoad() { super.viewDidLoad() textView.frame = view.bounds textView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(textView) // Editor chrome textView.showLineNumbers = true textView.lineSelectionDisplayType = .line // highlight current line textView.showPageGuide = true textView.pageGuideColumn = 80 // Invisible characters textView.showTabs = true textView.showSpaces = true textView.showLineBreaks = true // Typography textView.lineHeightMultiplier = 1.3 textView.kern = 0.3 // Indentation (4-space) textView.indentStrategy = .space(length: 4) // Line endings textView.lineEndings = .lf // Scrolling textView.verticalOverscrollFactor = 0.5 // Keyboard textView.autocorrectionType = .no textView.autocapitalizationType = .none textView.smartQuotesType = .no textView.smartDashesType = .no textView.editorDelegate = self } } extension EditorViewController: TextViewDelegate { func textViewDidChange(_ textView: TextView) { print("Content changed, length: \(textView.text.count)") } func textViewDidChangeSelection(_ textView: TextView) { // Update cursor-position indicator in toolbar if let loc = textView.textLocation(at: textView.selectedRange.location) { print("Line \(loc.lineNumber + 1), Col \(loc.column + 1)") } } } ``` -------------------------------- ### Prepare TextViewState on Background Thread Source: https://context7.com/simonbs/runestone/llms.txt Create `TextViewState` asynchronously to perform initial parsing off the main thread, improving UI responsiveness. ```swift import Runestone // Assume `JavaScriptLanguage` is a TreeSitterLanguage wrapping the JS grammar. // Assume `ColorfulTheme` conforms to `Theme`. func openDocument(text: String, textView: TextView) { DispatchQueue.global(qos: .userInitiated).async { let state = TextViewState( text: text, theme: ColorfulTheme(), language: JavaScriptLanguage() ) // Inspect auto-detected metadata switch state.detectedIndentStrategy { case .tab: print("File uses tabs") case .space(let length): print("File uses \(length)-space indentation") case .unknown: print("Indentation unknown") } if let endings = state.detectedLineEndings { print("Line endings: \(endings)") } DispatchQueue.main.async { textView.setState(state) // single, fast main-thread call } } } ``` -------------------------------- ### Initialize Tree-sitter Language Manually Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/AddingATreeSitterLanguage.md Create a Tree-sitter language instance by manually loading the parser and its associated query files. This is used when you have copied the Tree-sitter source files directly into your project. ```swift let text = "let foo = \"Hello World\"" let highlightsQuery = TreeSitterLanguage.Query(contentsOf: "queries/highlights.scm") let injectionsQuery = TreeSitterLanguage.Query(contentsOf: "queries/injections.scm") let language = TreeSitterLanguage(tree_sitter_javascript(), highlightsQuery: highlightsQuery, injectionsQuery: injectionsQuery) let state = TextViewState(text: text, language: language) textView.setState(state) ``` -------------------------------- ### Configure StringSyntaxHighlighter Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/SyntaxHighlightingAString.md Apply customizations such as kerning, line height multiplier, and tab length to the syntax highlighter instance. ```swift syntaxHighlighter.kern = 0.3 syntaxHighlighter.lineHeightMultiplier = 1.2 syntaxHighlighter.tabLength = 2 ``` -------------------------------- ### Syntax Highlighting a String Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/StringSyntaxHighlighter.md Use the `syntaxHighlight` method to apply syntax highlighting to a given string based on the highlighter's configuration. ```APIDOC ## ``StringSyntaxHighlighter/syntaxHighlight(_:)`` ### Description Applies syntax highlighting to the provided string using the configured theme and language. ### Parameters - **string** (`String`): The input string to be syntax highlighted. ### Returns An `AttributedString` with syntax highlighting applied. ``` -------------------------------- ### Gutter Configuration Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Configures the padding and width of the gutter. ```APIDOC ## Gutter ### Gutter - ``gutterLeadingPadding`` - ``gutterTrailingPadding`` - ``gutterWidth`` ``` -------------------------------- ### Appearance Configuration Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Configures the visual appearance of the TextView, including themes and colors. ```APIDOC ## Configuring the Appearance ### Configuring the Appearance - ``theme`` - ``backgroundColor`` - ``kern`` - ``lineHeightMultiplier`` - ``insertionPointColor`` - ``selectionBarColor`` - ``selectionHighlightColor`` - ``textContainerInset`` - ``Theme`` ``` -------------------------------- ### String Syntax Highlighting Source: https://context7.com/simonbs/runestone/llms.txt Generate a syntax-highlighted `NSAttributedString` from plain text without requiring a view, suitable for previews or rendering. ```APIDOC ## `StringSyntaxHighlighter` — Highlight Without a View Produce a syntax-highlighted `NSAttributedString` from a plain string with no view required — useful for previews, print rendering, or share sheets. ```swift import Runestone import UIKit func syntaxHighlightedAttributedString(code: String) -> NSAttributedString { let highlighter = StringSyntaxHighlighter( theme: OneDarkTheme(), language: makeSwiftLanguage() ) highlighter.lineHeightMultiplier = 1.4 highlighter.kern = 0.3 highlighter.tabLength = 4 return highlighter.syntaxHighlight(code) } // Use it in a UILabel or UITextView for a read-only preview let attrStr = syntaxHighlightedAttributedString(code: """ func greet(_ name: String) -> String { return \"Hello, \(name)!\" } """) previewLabel.attributedText = attrStr ``` ``` -------------------------------- ### TextViewDelegate Methods Source: https://context7.com/simonbs/runestone/llms.txt Implement these methods to customize the behavior of the TextView. ```APIDOC ## `TextViewDelegate` — Editor Event Callbacks The full `TextViewDelegate` protocol lets you respond to editing lifecycle, text changes, selection changes, character pair events, gutter width changes, and highlighted range navigation. ```swift import Runestone extension MyViewController: TextViewDelegate { // Gate editing (e.g., read-only mode) func textViewShouldBeginEditing(_ textView: TextView) -> Bool { !isReadOnly } // Validate every keystroke func textView(_ textView: TextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { guard text != "\n" else { return true } return text.unicodeScalars.allSatisfy { CharacterSet.alphanumerics.union(.whitespaces).contains($0) } } // Sync document model func textViewDidChange(_ textView: TextView) { document.text = textView.text document.markDirty() } // Update status bar func textViewDidChangeSelection(_ textView: TextView) { let range = textView.selectedRange statusBar.update(range: range) } // Looping search navigation feedback func textViewDidLoopToFirstHighlightedRange(_ textView: TextView) { showToast("Reached last match, looping to first") } // Allow highlighted-range replacement (e.g., find-replace toolbar) func textView(_ textView: TextView, canReplaceTextIn highlightedRange: HighlightedRange) -> Bool { true } func textView(_ textView: TextView, replaceTextIn highlightedRange: HighlightedRange) { textView.replace(highlightedRange.range, withText: replacementField.text ?? "") } } ``` ``` -------------------------------- ### Syntax Highlighting Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Manages syntax highlighting for different programming languages. ```APIDOC ## Syntax Highlighting ### Syntax Highlighting - ``setLanguageMode(_:completion:)`` - ``setState(_:addUndoAction:)`` - ``syntaxNode(at:)`` - ``redisplayVisibleLines()`` - ``TextViewState`` ``` -------------------------------- ### Navigate to Specific Line in Swift Source: https://context7.com/simonbs/runestone/llms.txt Navigates the text editor to a specified line by its zero-based index. Optionally selects the entire line or places the caret at the beginning. ```swift import Runestone // Go to line 42 (zero-based index 41) and select the entire line let didNavigate = textView.goToLine(41, select: .line) if !didNavigate { print("Line index out of range") } ``` ```swift // Go to line from a "Go To Line" dialog input (1-based user input) func goTo(humanLine: Int, textView: TextView) { guard humanLine > 0 else { return } textView.goToLine(humanLine - 1, select: .beginning) } ``` -------------------------------- ### Keyboard Management Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Manages keyboard appearance, type, return key, and input accessory views. ```APIDOC ## Managing the Keyboard ### Managing the Keyboard - ``keyboardAppearance`` - ``keyboardType`` - ``returnKeyType`` - ``inputAccessoryView`` - ``inputAssistantItem`` - ``reloadInputViews()`` ``` -------------------------------- ### Selecting Text Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Manages text selection properties and appearance. ```APIDOC ## Selecting Text ### Selecting Text - ``selectedRange`` - ``selectedTextRange`` - ``selectionBarColor`` - ``selectionHighlightColor`` ``` -------------------------------- ### Navigation Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Provides methods for navigating within the text content. ```APIDOC ## Navigation ### Navigation - ``TextLocation`` - ``textLocation(at:)`` - ``location(at:)`` - ``goToLine(_:select:)`` - ``GoToLineSelection`` - ``moveSelectedLinesUp()`` - ``moveSelectedLinesDown()`` - ``scrollRangeToVisible(_:)`` ``` -------------------------------- ### Implement TreeSitterLanguageProvider for Embedded Languages Source: https://context7.com/simonbs/runestone/llms.txt Implement `TreeSitterLanguageProvider` to supply sub-languages on demand, such as CSS and JavaScript within an HTML document. This requires defining a class that conforms to the protocol and returning the appropriate `TreeSitterLanguage` for requested language names. ```swift import Runestone final class HTMLLanguageProvider: TreeSitterLanguageProvider { func treeSitterLanguage(named languageName: String) -> TreeSitterLanguage? { switch languageName { case "javascript": return makeJavaScriptLanguage() case "css": return makeCSSLanguage() default: return nil } } } // Usage: func openHTMLFile(text: String, textView: TextView) { DispatchQueue.global(qos: .userInitiated).async { let provider = HTMLLanguageProvider() let state = TextViewState( text: text, theme: ColorfulTheme(), language: makeHTMLLanguage(), languageProvider: provider ) DispatchQueue.main.async { textView.setState(state) } } } ``` -------------------------------- ### Perform Text Search and Replace with SearchQuery Source: https://context7.com/simonbs/runestone/llms.txt Build a `SearchQuery` to find text matches within a `TextView`. Use `search(for:)` for finding and `search(for:replacingMatchesWith:)` combined with `replaceText(in:)` for find-and-replace operations, supporting plain text and regular expressions with capture group substitution. ```swift import Runestone // Plain substring search let query = SearchQuery(text: "TODO", matchMethod: .contains, isCaseSensitive: false) let results = textView.search(for: query) print("Found \(results.count) match(es)") // Highlight all matches textView.highlightedRanges = results.map { HighlightedRange(range: $0.range, color: .systemYellow.withAlphaComponent(0.4), cornerRadius: 3) } // Regex replace: capture group substitution ($1 back-reference) let regexQuery = SearchQuery( text: #"print\("(.+?)\"\)""#, matchMethod: .regularExpression, isCaseSensitive: true ) let replaceResults = textView.search(for: regexQuery, replacingMatchesWith: "NSLog($1)") let replacements = replaceResults.map { BatchReplaceSet.Replacement(range: $0.range, text: $0.replacementText) } textView.replaceText(in: BatchReplaceSet(replacements: replacements)) ``` -------------------------------- ### Laying Out Subviews Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Handles layout of subviews and safe area insets. ```APIDOC ## Laying Out Subviews ### Laying Out Subviews - ``layoutSubviews()`` - ``safeAreaInsetsDidChange()`` ``` -------------------------------- ### Syntax Highlight a String Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/SyntaxHighlightingAString.md Use the configured syntax highlighter to process a string and return an attributed string with syntax highlighting applied. The resulting attributed string can be displayed in a UILabel or UITextView. ```swift let attributedString = syntaxHighlighter.syntaxHighlight( """ function fibonacci(num) { if (num <= 1) { return 1 } return fibonacci(num - 1) + fibonacci(num - 2) } """) ``` -------------------------------- ### Line Wrapping Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Enables and configures line wrapping behavior. ```APIDOC ## Line Wrapping ### Line Wrapping - ``isLineWrappingEnabled`` - ``lineBreakMode`` ``` -------------------------------- ### Add Runestone Dependency via Swift Package Manager Source: https://context7.com/simonbs/runestone/llms.txt Integrate Runestone into your project by adding its Git repository URL to your `Package.swift` file. ```swift // Package.swift let package = Package( dependencies: [ .package(url: "git@github.com:simonbs/Runestone.git", from: "0.1.0") ], targets: [ .target(name: "MyApp", dependencies: ["Runestone"]) ] ) ``` -------------------------------- ### Set TextView State Asynchronously Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/GettingStarted.md Initialize TextViewState on a background queue to avoid blocking the main thread, then update the TextView on the main thread. ```swift DispatchQueue.global(qos: .userInitiated).async { // Initialize of TextViewState on a background queue to due avoid blocking the main thread. let text = "let foo = \"Hello World\"" let state = TextViewState(text: text, theme: ColorfulTheme(), language: .javaScript) DispatchQueue.main.async { // setState(_:) should be called on the main thread. textView.setState(state) } } ``` -------------------------------- ### Line Navigation Source: https://context7.com/simonbs/runestone/llms.txt Navigate the editor to a specific line by its zero-based index, with options to place the caret at the beginning, end, or select the entire line. ```APIDOC ## `goToLine(_:select:)` — Line Navigation Jump the editor to any line by zero-based index, placing the caret at the beginning, end, or selecting the whole line. ```swift import Runestone // Go to line 42 (zero-based index 41) and select the entire line let didNavigate = textView.goToLine(41, select: .line) if !didNavigate { print("Line index out of range") } // Go to line from a "Go To Line" dialog input (1-based user input) func goTo(humanLine: Int, textView: TextView) { guard humanLine > 0 else { return } textView.goToLine(humanLine - 1, select: .beginning) } ``` ``` -------------------------------- ### Apply Detected Indent Strategy in Swift Source: https://context7.com/simonbs/runestone/llms.txt Applies the detected indent strategy (tab or space) to the text view. Defaults to 2 spaces if the strategy is unknown. ```swift import Runestone // After opening a file, apply the detected indent strategy func applyDetectedIndent(to textView: TextView, from state: TextViewState) { switch state.detectedIndentStrategy { case .tab: textView.indentStrategy = .tab(length: 4) case .space(let length): textView.indentStrategy = .space(length: length) case .unknown: textView.indentStrategy = .space(length: 2) // project default } } ``` -------------------------------- ### UITextInput Conformance Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Conforms to the UITextInput protocol for text input functionalities. ```APIDOC ## UITextInput Conformace ### UITextInput Conformace - ``hasText`` - ``beginningOfDocument`` - ``endOfDocument`` - ``markedTextRange`` - ``tokenizer`` - ``textRange(from:to:)`` - ``position(from:offset:)`` - ``position(from:in:offset:)`` - ``position(within:farthestIn:)`` - ``closestPosition(to:)`` - ``closestPosition(to:within:)`` - ``compare(_:to:)`` - ``offset(from:to:)`` - ``characterRange(at:)`` - ``characterRange(byExtending:in:)`` - ``caretRect(for:)`` - ``firstRect(for:)`` - ``selectionRects(for:)`` ``` -------------------------------- ### Text View Delegate Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Handles text view changes through the TextViewDelegate protocol. ```APIDOC ## Responding to Text View Changes ### Responding to Text View Changes - ``editorDelegate`` - ``TextViewDelegate`` ``` -------------------------------- ### Enable Native Find Panel in Swift (iOS 16+) Source: https://context7.com/simonbs/runestone/llms.txt Enables the system's native find-and-replace panel for the text view. The panel can be presented with or without the replace bar. ```swift import Runestone @available(iOS 16, *) func enableNativeFindPanel(textView: TextView) { textView.isFindInteractionEnabled = true // Present find panel (no replace bar) textView.findInteraction?.presentFindNavigator(showingReplace: false) // Present find-and-replace panel textView.findInteraction?.presentFindNavigator(showingReplace: true) } ``` -------------------------------- ### Generate Syntax Highlighted Attributed String in Swift Source: https://context7.com/simonbs/runestone/llms.txt Creates a syntax-highlighted NSAttributedString from a plain string without needing a text view. Useful for previews or rendering. ```swift import Runestone import UIKit func syntaxHighlightedAttributedString(code: String) -> NSAttributedString { let highlighter = StringSyntaxHighlighter( theme: OneDarkTheme(), language: makeSwiftLanguage() ) highlighter.lineHeightMultiplier = 1.4 highlighter.kern = 0.3 highlighter.tabLength = 4 return highlighter.syntaxHighlight(code) } ``` ```swift // Use it in a UILabel or UITextView for a read-only preview let attrStr = syntaxHighlightedAttributedString(code: """ func greet(_ name: String) -> String { return \"Hello, \(name)!\" } """) previewLabel.attributedText = attrStr ``` -------------------------------- ### Highlighting Text Ranges Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Manages highlighted text ranges and navigation between them. ```APIDOC ## Highlighting Text Ranges ### Highlighting Text Ranges - ``highlightedRanges`` - ``highlightedRangeLoopingMode`` - ``selectNextHighlightedRange()`` - ``selectPreviousHighlightedRange()`` - ``selectHighlightedRange(at:)`` ``` -------------------------------- ### Native Find Panel Source: https://context7.com/simonbs/runestone/llms.txt Enable and interact with the system's native find-and-replace panel (available on iOS 16 and later) for seamless text searching. ```APIDOC ## `isFindInteractionEnabled` / `findInteraction` — Native Find Panel (iOS 16+) Enable the system find-and-replace panel that slides in from the top of the keyboard. ```swift import Runestone @available(iOS 16, *) func enableNativeFindPanel(textView: TextView) { textView.isFindInteractionEnabled = true // Present find panel (no replace bar) textView.findInteraction?.presentFindNavigator(showingReplace: false) // Present find-and-replace panel textView.findInteraction?.presentFindNavigator(showingReplace: true) } ``` ``` -------------------------------- ### C Header for Tree-sitter Parser Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/AddingATreeSitterLanguage.md Define the C function signature for a Tree-sitter parser. This header file should be imported into your bridging header. ```c #ifdef __cplusplus extern "C" { #endif typedef struct TSLanguage TSLanguage; // Replace {language} with the name of the parser you are importing. const TSLanguage *tree_sitter_{language}(void); #ifdef __cplusplus } #endif ``` -------------------------------- ### Shift Lines Left/Right and Move Lines Up/Down in Swift Source: https://context7.com/simonbs/runestone/llms.txt Provides functions to shift selected lines left or right by one indent level, and to move selected lines up or down. These are typically wired to toolbar buttons or keyboard shortcuts. ```swift // Toolbar buttons wired to shift actions @objc func shiftLeft() { textView.shiftLeft() } @objc func shiftRight() { textView.shiftRight() } ``` ```swift // Move selected lines up/down (e.g., Alt+Up / Alt+Down) @objc func moveLinesUp() { textView.moveSelectedLinesUp() } @objc func moveLinesDown() { textView.moveSelectedLinesDown() } ``` ```swift // Read current indent strategy at any time let detected = textView.detectIndentStrategy() print("Current file: \(detected)") ``` -------------------------------- ### Find and Replace Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Supports find and replace functionality within the text view. ```APIDOC ## Supporting Find and Replace ### Supporting Find and Replace - ``isFindInteractionEnabled`` - ``findInteraction`` - ``search(for:)`` - ``search(for:replacingMatchesWith:)`` - ``textPreview(containing:)`` ``` -------------------------------- ### Scrolling Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Controls scrolling behavior and content offset. ```APIDOC ## Scrolling ### Scrolling - ``contentOffset`` - ``isAutomaticScrollEnabled`` ``` -------------------------------- ### Configure Auto-Closing Brackets with CharacterPair Source: https://context7.com/simonbs/runestone/llms.txt Conform to `CharacterPair` and assign instances to `textView.characterPairs` to enable automatic insertion of closing characters for brackets and quotes. Configure deletion behavior and use `syntaxNode(at:)` in the delegate to prevent insertion within string literals. ```swift import Runestone struct BasicPair: CharacterPair { let leading: String let trailing: String } // Configure pairs before setting state textView.characterPairs = [ BasicPair(leading: "(", trailing: ")"), BasicPair(leading: "[", trailing: "]"), BasicPair(leading: "{", trailing: "}"), BasicPair(leading: "\"", trailing: "\""), BasicPair(leading: "'", trailing: "'"), BasicPair(leading: "`", trailing: "`") ] // Automatically delete trailing ) when the matching ( is deleted textView.characterPairTrailingComponentDeletionMode = .immediatelyFollowingLeadingComponent // Use syntaxNode(at:) in the delegate to skip pair insertion inside strings extension MyEditorDelegate: TextViewDelegate { func textView(_ textView: TextView, shouldInsert characterPair: CharacterPair, in range: NSRange) -> Bool { guard characterPair.leading == "\"" || characterPair.leading == "'" else { return true } // Don't insert quote pairs when caret is already inside a string literal if let node = textView.syntaxNode(at: range.location), node.type == "string" { return false } return true } } ``` -------------------------------- ### Add Runestone Dependency Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/GettingStarted.md Add the Runestone package to your project's dependencies in Package.swift. ```swift let package = Package( dependencies: [ .package(url: "git@github.com:simonbs/Runestone.git", from: "0.1.0") ] ) ``` -------------------------------- ### Line Selection Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Controls the display type for line selection. ```APIDOC ## Line Selection ### Line Selection - ``lineSelectionDisplayType`` ``` -------------------------------- ### Highlight Text Ranges with HighlightedRange Source: https://context7.com/simonbs/runestone/llms.txt Mark arbitrary text ranges with colored backgrounds and optional rounded corners using `HighlightedRange`. This can be used to visually indicate search results or other important text segments. Enable looping navigation and step through matches using `selectNextHighlightedRange()`. ```swift import Runestone // Highlight all occurrences after a search func highlightOccurrences(of term: String, in textView: TextView) { let query = SearchQuery(text: term, matchMethod: .contains, isCaseSensitive: false) let results = textView.search(for: query) textView.highlightedRanges = results.enumerated().map { HighlightedRange( id: "match-\(index)", range: result.range, color: .systemOrange.withAlphaComponent(0.3), cornerRadius: 4 ) } // Enable looping navigation textView.highlightedRangeLoopingMode = .enabled // Step through matches textView.selectNextHighlightedRange() } ``` -------------------------------- ### Indenting Text Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Manages text indentation, including shifting and detection of indentation strategy. ```APIDOC ## Indenting Text ### Indenting Text - ``shiftLeft()`` - ``shiftRight()`` - ``isIndentation(at:)`` - ``detectIndentStrategy()`` - ``indentStrategy`` - ``IndentStrategy`` ``` -------------------------------- ### Convert Line/Column to Text Offset in Swift Source: https://context7.com/simonbs/runestone/llms.txt Converts a structured TextLocation (line and column number) to a flat character offset within the text. Used for programmatically moving the caret. ```swift // Programmatically place caret at line 5, column 0 func moveCaret(textView: TextView, toLine line: Int, column: Int) { let target = TextLocation(lineNumber: line, column: column) if let offset = textView.location(at: target) { textView.selectedRange = NSRange(location: offset, length: 0) } } ``` -------------------------------- ### Line Endings Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Specifies the line ending format. ```APIDOC ## Line Endings ### Line Endings - ``lineEndings`` ``` -------------------------------- ### Find Longest Match for Highlight Name Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/CreatingATheme.md Implement this function to determine the most specific highlight name supported by your theme. It iterates through highlight name components to find the longest matching supported name. ```swift func findLongestMatch(highlightName: String) -> String? { // Split the highlight name on dots so "function.builtin.static" becomes ["function", "builtin", "static"] var components = highlightName.components(separatedBy: ".") // Loop through components to find the longest match. while !components.isEmpty { // Join all components with a dot so ["function", "builtin", "static"] becomes "function.builtin.static" let candidate = components.joined(separator: ".") if supportedHighlightNames.contains(candidate) { // The candidate highlight name is supported. return candidate } else { // Remove the last component so our next candidate will be one component shorter in the next iteration of the loop. components.removeLast() } } // No match found for the highlight name. return nil } ``` -------------------------------- ### Convert Text Offset to Line/Column in Swift Source: https://context7.com/simonbs/runestone/llms.txt Converts a flat character offset within the text to a structured TextLocation (line and column number). Useful for displaying the current caret position. ```swift import Runestone // Show "Ln 12, Col 7" in a toolbar for the current caret func updateCursorLabel(textView: TextView, label: UILabel) { let offset = textView.selectedRange.location if let loc = textView.textLocation(at: offset) { label.text = "Ln \(loc.lineNumber + 1), Col \(loc.column + 1)" } } ``` -------------------------------- ### Overscroll Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Configures the overscroll behavior for vertical and horizontal scrolling. ```APIDOC ## Overscroll ### Overscroll - ``verticalOverscrollFactor`` - ``horizontalOverscrollFactor`` ``` -------------------------------- ### Responder Chain Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Manages the text view's ability to become and resign first responder status. ```APIDOC ## Responder Chain ### Responder Chain - ``canBecomeFirstResponder`` - ``becomeFirstResponder()`` - ``resignFirstResponder()`` ``` -------------------------------- ### Character Pairs Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Manages character pairs and their deletion behavior. ```APIDOC ## Character Pairs ### Character Pairs - ``characterPairs`` - ``characterPairTrailingComponentDeletionMode`` ``` -------------------------------- ### Indentation Control Source: https://context7.com/simonbs/runestone/llms.txt Control line indentation by shifting selected lines left or right, and automatically apply the indent strategy detected in the document. ```APIDOC ## Indentation Control — `shiftLeft`, `shiftRight`, `detectIndentStrategy` Shift selected lines left/right by one indent level, and auto-apply the indent strategy detected in the opened document. ```swift import Runestone // After opening a file, apply the detected indent strategy func applyDetectedIndent(to textView: TextView, from state: TextViewState) { switch state.detectedIndentStrategy { case .tab: textView.indentStrategy = .tab(length: 4) case .space(let length): textView.indentStrategy = .space(length: length) case .unknown: textView.indentStrategy = .space(length: 2) // project default } } // Toolbar buttons wired to shift actions @objc func shiftLeft() { textView.shiftLeft() } @objc func shiftRight() { textView.shiftRight() } // Move selected lines up/down (e.g., Alt+Up / Alt+Down) @objc func moveLinesUp() { textView.moveSelectedLinesUp() } @objc func moveLinesDown() { textView.moveSelectedLinesDown() } // Read current indent strategy at any time let detected = textView.detectIndentStrategy() print("Current file: \(detected)") ``` ``` -------------------------------- ### Coordinate Conversion Source: https://context7.com/simonbs/runestone/llms.txt Convert between flat character offsets and structured `TextLocation` (line and column) values for precise text manipulation. ```APIDOC ## `textLocation(at:)` / `location(at:)` — Coordinate Conversion Convert between flat character offsets and structured `TextLocation` (line + column) values. ```swift import Runestone // Show "Ln 12, Col 7" in a toolbar for the current caret func updateCursorLabel(textView: TextView, label: UILabel) { let offset = textView.selectedRange.location if let loc = textView.textLocation(at: offset) { label.text = "Ln \(loc.lineNumber + 1), Col \(loc.column + 1)" } } // Programmatically place caret at line 5, column 0 func moveCaret(textView: TextView, toLine line: Int, column: Int) { let target = TextLocation(lineNumber: line, column: column) if let offset = textView.location(at: target) { textView.selectedRange = NSRange(location: offset, length: 0) } } ``` ``` -------------------------------- ### Editing Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Provides methods for editing text, including insertion, replacement, and deletion. ```APIDOC ## Editing ### Editing - ``isEditable`` - ``isSelectable`` - ``isEditing`` - ``text`` - ``autocapitalizationType`` - ``autocorrectionType`` - ``spellCheckingType`` - ``smartDashesType`` - ``smartInsertDeleteType`` - ``smartQuotesType`` - ``text(in:)-3lp4v`` - ``text(in:)-3wzco`` - ``insertText(_:)`` - ``replaceText(in:)`` - ``replace(_:withText:)-7gret`` - ``replace(_:withText:)-7ugo8`` - ``deleteBackward()`` - ``undoManager`` ``` -------------------------------- ### Invisible Characters Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Controls the visibility and display of invisible characters. ```APIDOC ## Invisible Characters ### Invisible Characters - ``showSpaces`` - ``showNonBreakingSpaces`` - ``showTabs`` - ``showLineBreaks`` - ``showSoftLineBreaks`` - ``spaceSymbol`` - ``nonBreakingSpaceSymbol`` - ``tabSymbol`` - ``lineBreakSymbol`` - ``softLineBreakSymbol`` ``` -------------------------------- ### Line Numbers Source: https://github.com/simonbs/runestone/blob/main/Sources/Runestone/Documentation.docc/Extensions/TextView.md Toggles the display of line numbers. ```APIDOC ## Line Numbers ### Line Numbers - ``showLineNumbers`` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.