Skip to content

zcohan/TextActions

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

2 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

TextActions

Swift 5.9+ Platforms SPM Compatible

Transform text editing operations into precise line-based diffs for array-backed text models.

What is TextActions?

TextActions bridges the gap between text views and array-based document models by analyzing text changes and producing structured diffs with exact line indexes for insertions, deletions, and edits.

When you have a text view displaying content from an array-based model (like an array of strings representing lines), changes made in the text view need to be translated back to the underlying model. TextActions does exactly that - it takes a text editing operation (like typing, deleting, or pasting) and tells you precisely which array elements need to be inserted, deleted, or modified.

Key Features

  • ๐ŸŽฏ Precise Analysis: Converts text editing operations to exact line-based array operations
  • โšก High Performance: Uses binary search and efficient algorithms for line indexing
  • ๐Ÿง  Smart Logic: Handles complex scenarios like multi-line pastes, line merging, and boundary cases
  • ๐Ÿ“ฑ Cross-Platform: Works on macOS 11+, iOS 13+, and other Swift platforms
  • ๐Ÿ” Well-Tested: Comprehensive test suite with hundreds of edge cases
  • ๐Ÿ—๏ธ Foundation-Light: Minimal dependencies for maximum compatibility

Quick Start

import TextActions

let originalText = """
Line 1
Line 2
Line 3
"""

// Create an indexed view of the text
let indexedString = IndexedString(contents: originalText)

// User selects "Line 2\n" and types "New Line\nAnother Line\n"
let selectedRange = NSRange(location: 7, length: 7) // "Line 2\n"
let replacementString = "New Line\nAnother Line\n"

// Analyze what needs to change in your array model
let action = TextEditingActionAnalyzer.textEditingActionGiven(
    selectedRange: selectedRange,
    replacementString: replacementString,
    in: indexedString
)

// Results tell you exactly what to do:
print(action.kind)           // .editMultipleLines
print(action.linesEdited)    // [1] (index 1 gets edited)
print(action.linesInserted)  // [2] (insert new line at index 2)
print(action.linesDeleted)   // [] (no lines deleted)

// Your array transformation:
// lines[1] = "New Line"
// lines.insert("Another Line", at: 2)

Core Concepts

IndexedString

An efficient wrapper around strings that provides line-based indexing and fast lookups:

let indexedString = IndexedString(contents: multilineText)

// Access lines by index
let firstLine = indexedString[0, .LineContents]    // Content without newline
let firstLineFull = indexedString[0, .Line]        // Content with newline

// Find which line contains a character position
let lineIndex = indexedString.indexOfLineContainingLocation(50)

// Get line ranges
let lineRange = indexedString.rangeOfLineAtIndex(2)

TextEditingAction

The result of analyzing a text editing operation:

public enum TextEditingActionKind {
    case insertSingleLine       // Adding one new line
    case insertMultipleLines    // Adding multiple new lines  
    case removeSingleLine       // Removing one line
    case removeMultipleLines    // Removing multiple lines
    case editSingleLine         // Modifying content of one line
    case editMultipleLines      // Complex edit affecting multiple lines
    case replaceAll            // Selecting all and replacing
    case clearAll              // Selecting all and deleting
    case unidentifiedAction    // Fallback case
}

// Each action contains precise IndexSets:
action.linesEdited      // Which existing lines need content updates
action.linesInserted    // Which new lines to insert (and where)
action.linesDeleted     // Which existing lines to remove

Advanced Usage

Handling Complex Edits

TextActions excels at handling complex editing scenarios:

// Multi-line paste over a selection
let selectedRange = NSRange(location: 10, length: 25)  // Spans multiple lines
let pastedText = "First\nSecond\nThird"

let action = TextEditingActionAnalyzer.textEditingActionGiven(
    selectedRange: selectedRange,
    replacementString: pastedText,
    in: indexedString
)

// Results might be:
// - action.linesEdited: [1, 3] (modify first and last affected lines)
// - action.linesInserted: [2] (insert new line in middle)
// - action.linesDeleted: [4, 5] (remove lines that were fully selected)

Custom Text Models

Implement TextEditingActionContext for custom text representations:

extension MyTextModel: TextEditingActionContext {
    public var lineCount: Int { lines.count }
    
    public func indexesOfLinesTouchedBy(range: NSRange) -> IndexSet {
        // Your line detection logic
    }
    
    public func rangeOfLineAt(index: Int, includeEndOfLine: Bool) -> NSRange {
        // Your range calculation logic  
    }
    
    // ... implement other required methods
}

// Now you can analyze edits directly on your model
let action = TextEditingActionAnalyzer.textEditingActionGiven(
    selectedRange: range,
    replacementString: text,
    in: myTextModel
)

Installation

Swift Package Manager

Add TextActions to your project via Xcode:

  1. File โ†’ Add Package Dependencies...
  2. Enter: https://github.com/YourUsername/TextActions

Or add it to your Package.swift:

dependencies: [
    .package(url: "https://github.com/YourUsername/TextActions", from: "1.0.0")
]

Use Cases

TextActions is perfect for:

  • ๐Ÿ“ Text Editors: Apps with line-based text editing (code editors, note apps)
  • ๐Ÿ“Š Document Models: Converting text changes to structured document updates
  • ๐Ÿ”„ Undo/Redo Systems: Tracking precise changes for granular undo operations
  • ๐Ÿ“ฑ Collaborative Editing: Converting user edits to operational transforms
  • ๐Ÿงฎ Calculation Apps: Text-based interfaces backed by expression arrays (like Soulver)
  • ๐Ÿ“‹ List Editors: Any app where text view content maps to array elements

Requirements

  • Swift 5.9+
  • macOS 11.0+ / iOS 13.0+
  • No external dependencies beyond Foundation

Performance

TextActions is built for performance:

  • Binary Search: O(log n) line lookups using efficient caching
  • Minimal Allocations: Reuses data structures where possible
  • Smart Caching: Line metrics calculated once and cached
  • Foundation-Light: Minimal dependency footprint

Contributing

We welcome contributions! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for your changes
  4. Ensure all tests pass
  5. Submit a pull request

License

TextActions is available under the MIT license. See LICENSE for details.

Credits

Originally developed by Zac Cohan as part of the Soulver calculation engine. Extracted into a standalone package to benefit the broader Swift community.


Transform your text editing operations into precise array diffs with TextActions.

About

Transform text editing operations into precise line-based diffs for array-backed text models

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •  

Languages