{"id":13793666,"url":"https://github.com/krzyzanowskim/STTextView","last_synced_at":"2025-05-12T20:31:12.344Z","repository":{"id":37491958,"uuid":"460095046","full_name":"krzyzanowskim/STTextView","owner":"krzyzanowskim","description":"Performant and reusable text view component (TextKit 2), with line numbers and more. UITextView / NSTextView replacement.","archived":false,"fork":false,"pushed_at":"2025-05-05T09:06:56.000Z","size":4134,"stargazers_count":1259,"open_issues_count":0,"forks_count":77,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-05-05T10:25:12.936Z","etag":null,"topics":["appkit","cococa","editor","macos","nstextview","sttextview","swift","swiftui","text","textkit","textkit2","textview"],"latest_commit_sha":null,"homepage":"https://swift.best","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/krzyzanowskim.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["krzyzanowskim"]}},"created_at":"2022-02-16T16:53:58.000Z","updated_at":"2025-05-05T09:07:00.000Z","dependencies_parsed_at":"2023-11-25T14:02:51.573Z","dependency_job_id":"c0e6de93-e5da-4a5a-a720-92a2c070b6fa","html_url":"https://github.com/krzyzanowskim/STTextView","commit_stats":{"total_commits":844,"total_committers":14,"mean_commits":"60.285714285714285","dds":0.02962085308056872,"last_synced_commit":"984e18c6ea7b3f958cc30eaf4e75009702c10d97"},"previous_names":[],"tags_count":97,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krzyzanowskim%2FSTTextView","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krzyzanowskim%2FSTTextView/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krzyzanowskim%2FSTTextView/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krzyzanowskim%2FSTTextView/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/krzyzanowskim","download_url":"https://codeload.github.com/krzyzanowskim/STTextView/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253562860,"owners_count":21927961,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appkit","cococa","editor","macos","nstextview","sttextview","swift","swiftui","text","textkit","textkit2","textview"],"created_at":"2024-08-03T23:00:28.612Z","updated_at":"2025-05-12T20:31:12.303Z","avatar_url":"https://github.com/krzyzanowskim.png","language":"Swift","readme":"\u003cimg height=\"45\" src=\"https://user-images.githubusercontent.com/758033/235909140-3589bb7d-51a0-4df3-8d71-2dc30fcabc8c.png\"\u003e\n\n# STTextView\n\nPerformant [macOS](https://www.apple.com/macos) and [iOS](https://www.apple.com/ios) TextView with line numbers and much more. (NSTextView/UITextView reimplementation)\n\nThe goal of this project is to build [NSTextView](https://developer.apple.com/documentation/appkit/nstextview)/[UITextView](https://developer.apple.com/documentation/uikit/uitextview) replacement reusable component utilizing [TextKit 2](https://developer.apple.com/videos/play/wwdc2021/10061/) framework. [due to many good reasons](#-textkit-2-bug-reports-list).\n\nThe component is mainly developed to serve [Swift Studio](https://swiftstudio.app) needs as a **source code editor**.\n\n\n![Poster](https://github.com/user-attachments/assets/58b1a58b-d8bd-44d0-9946-2336335f3b0d)\n\n[TextKit 2](https://developer.apple.com/forums/tags/wwdc21-10061) was announced during [WWDC 2021](https://developer.apple.com/videos/play/wwdc2021/10061/) as a TextKit 1 replacement for text layout and whatnot. Apple announced that `NSTextView`, the view component specialized for text editing, will adopt TextKit 2 and provide support along TextKit 1 bit. As I started to learn more about `NSTextView` + TextKit2, I realized that as of today (Feb 2022), neither `NSTextView` nor TextKit 2 classes are fully functional. Along the way, I reported several bug reports to Apple requesting DTS (support tickets). Eventually, I got blocked by specific bugs that pushed me to start this project.\n\n## ✨ Features\n\n- macOS text system integration\n- Performant Text editing\n- Display line numbers\n- Display invisible characters\n- Customization of colors and fonts\n- Toggle line wrapping on and off\n- Adjust the height of lines\n- Highlight/Select ranges in the text view\n- Multi-cursor editing\n- Search/Replace the text\n- Spelling and Grammar\n- Dictation\n- Customizable Completion support\n- Smooth scrolling of long content\n- LTR (Left To Right) / RTL (Right To Left) layout\n- Undo/Redo\n- Plugins\n- Anchored annotations (via plugin)\n- Source Code syntax highlighting (via plugin)\n\n\u003cdiv align=\"center\"\u003e\n  \u003cvideo src=\"https://github.com/user-attachments/assets/e18c058b-8a58-47e0-a57c-a3b01f3d93db\" width=\"90%\" /\u003e\n\u003c/div\u003e\n\n## 🤝 Support \u0026 Sponsors\n\nThe financial sustainability of the project is possible thanks to the ongoing contributions from our [GitHub Sponsors](https://github.com/sponsors/krzyzanowskim)\n\n## 🗓️ Roadmap\n\n**Suggest** or **vote** for new features: [Feature Requests](https://github.com/krzyzanowskim/STTextView/discussions/14)\n\n## 🚀 Getting Started\n\n`STTextView` is distributed using the [Swift Package Manager](https://www.swift.org/package-manager/). Install it in a project by adding it as a dependency in your `Package.swift` manifest or through “Package Dependencies” in Xcode project settings\n\n```swift\nlet package = Package(\n    dependencies: [\n        .package(url: \"https://github.com/krzyzanowskim/STTextView\", from: \"2.0.0\")\n    ]\n)\n```\n\n## Demo Application\n\nThe demo applications [TextEdit](TextEdit) and [TextEdit.SwiftUI](TextEdit.SwiftUI) lets you explore the library.\n\n## Plugins\n\nPlugins in an STTextView component offer additional functionalities and customizations beyond the simple text display. They enhance the core capabilities of the text view by adding features such as syntax highlighting, word count tracking, and more. These plugins expand the STTextView's utility while maintaining a modular and adaptable software structure.\n\n- [Plugin-Neon](https://github.com/krzyzanowskim/STTextView-Plugin-Neon) Source Code Syntax Highlighting with [TreeSitter](https://tree-sitter.github.io/tree-sitter/) and [Neon](https://github.com/ChimeHQ/Neon).\n- [Plugin-TextFormation](https://github.com/krzyzanowskim/STTextView-Plugin-TextFormation) Typing completions with [TextFormation](https://github.com/ChimeHQ/TextFormation).\n- [Plugin-Annotations](https://github.com/krzyzanowskim/STTextView-Plugin-Annotations) Anchored annotations (eg. inlined error message)) plugin.\n- [Plugin-Template](https://github.com/krzyzanowskim/STTextView-Plugin-Template) Dummy plugin template repository ready to build new plugin.\n- ... [add more](https://github.com/topics/sttextview) plugins\n\n## Usage\n\n### SwiftUI\n\nThe `TextView` is a [SwiftUI](https://developer.apple.com/xcode/swiftui/) view that wraps the STTextView.\n\n* Support for rich text (attributed string)\n* Faster than SwiftUI.TextEdit (https://twitter.com/krzyzanowskim/status/1677628085217243137)\n\n```swift\nimport STTextViewSwiftUI\n\nstruct ContentView: View {\n\n    @State private var text = AttributedString(\"Hello World!\")\n    @State private var selection: NSRange?\n\n    var body: some View {\n        TextView(\n            text: $text,\n            selection: $selection,\n            options: [.wrapLines, .highlightSelectedLine],\n            plugins: [plugin1(), plugin2()]\n        )\n        .textViewFont(.preferredFont(forTextStyle: .body))\n    }\n}\n```\n\n### Create a TextView\n\nThe `STTextView` is an `NSView` subclass and can be initialized like any other view. It has an API that is similar to that of NSTextView.\n\n```swift\nimport STTextView\n\nlet textView = STTextView()\nview.addSubview(textView)\n```\n\n(macOS) add to scroll view\n\n```swift\nlet textView = STTextView()\nlet scrollView = NSScrollView()\nscrollView.documentView = textView\n```\n\n```swift\nlet scrollView = STTextView.scrollableTextView()\nlet textView = scrollView.documentView as! STTextView\n```\n\n### Customize\n\nThe text view can be customized in a variety of ways.\n\n```swift\nlet paragraph = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle\n// Set the line-height to 120%\nparagraph.lineHeightMultiple = 1.2\nparagraph.defaultTabInterval = 28\n\n// Default Paragraph style\ntextView.typingAttributes[.paragraphStyle] = paragraph\n\n// Set default font\ntextView.font = NSFont.monospacedSystemFont(ofSize: 14, weight: .regular)\n\n// Set default text color\ntextView.textColor = .textColor\n\n// Set text value\ntextView.text = \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ornare lobortis sem a vulputate.\"\ntextView.addAttributes([.foregroundColor: NSColor.red], range: NSRange(location: 10, length: 5))\n\n// Wrap/No wrap lines to editor width\ntextView.isHorizontallyResizable = true\n\n// Highlight the selected line.\ntextView.highlightSelectedLine = true\n```\n\nAdd gutter with line numbers\n\n```swift\ntextView.showLineNumbers = true\ntextView.gutterView?.drawSeparator = true\n```\n\n(macOS) Enable an optional search-and-replace find interface inside a view, usually a scroll view.\n\n```swift\ntextView.isIncrementalSearchingEnabled = true\ntextView.textFinder.incrementalSearchingShouldDimContentView = true\n```\n\n## 🐛 TextKit 2 Bug Reports List\n\nList of **TextKit 2** issues and bugs related to NSTextView and the TextKit framework I reported to Apple so far:\n\n- FB9856587: TextKit2 unexpected additional line fragment for the last line\n- FB9925766: NSTextSelectionNavigation.deletionRanges only works at the end of the word\n- FB9925647: NSTextLayoutManager.replaceContents(in range: with attributedString:) is documented but is not part of the public API\n- FB9907261: NSTextElementProvider.replaceContents(in:with:) does not replace content as documented\n- FB9692714: Rendering attributes do not draw properly\n- FB9886911: NSTextView can't properly layout and display long lines (this one is nasty since it causes the view to \"jump\" whenever text attribute updates)\n- FB9713415: NSTextView drawInsertionPoint(in:color:turnedOn) is never called\n- FB9971054: NSLayoutManager.enumerateCaretOffsetsInLineFragment ignores starting location\n- FB9971054: NSTextView assert on selection when setup with TextKit2\n- FB9743449, FB10019859: NSTextContentStorage.textElements(for:) returns no element, while enumerateTextElements does return elements\n- FB11898356: textSelections(interactingAt:inContainerAt:anchors:modifiers:selecting:bounds:) produces wrong selections for certain locations\n- FB12726775: Documentation to the NSTextParagraph.paragraphContentRange is incorrect\n- FB13272586: NSTextContainer.size default value is not as documented\n- [FB13290979](https://gist.github.com/krzyzanowskim/7adc5ee66be68df2f76b9752476aadfb): NSTextContainer.lineFragmentPadding does not affect end of the fragment usageBoundsForTextContainer rectangle\n- [FB13291926](https://gist.github.com/krzyzanowskim/33a2478fa2281b77816acb7a7f6f77ac): NSTextLayoutManager.usageBoundsForTextContainer observer is never trigerred\n- [FB13789916](https://gist.github.com/krzyzanowskim/340c5810fc427e346b7c4b06d46b1e10): NSTextInputClient.setMarkedText provide bogus selection range for Chinese keyboard\n- [FB14700414](https://gist.github.com/krzyzanowskim/0a83eb9d5303016b277920a6b7c9f9fc): NSTextList doesn't work since macOS 14 (regression)\n- [FB15131180](https://gist.github.com/krzyzanowskim/510ecf8df259d779e22df8ad13c256c0): TextKit extra line frame is incorrect and does not respect layout fragment size (regression)\n- [FB17020435](https://gist.github.com/krzyzanowskim/da247e1a9f5f94f0e14a1e3047b86e59): enumerateCaretOffsetsInLineFragmentAtLocation:usingBlock: documentation is not accurate \n\n... I'm aware that the list of issues is not complete. I managed to workaround most of the problems in STTextView.\n\n## Why ST?\n\n(**ST** prefix stands for \"**S**wift s**T**udio\" because **[SS](https://en.wikipedia.org/wiki/Schutzstaffel)** is not good prefix since 1939)\n\n## Suggestions or Feedback\n\nStart a new [discussion topic](https://github.com/krzyzanowskim/STTextView/discussions) or a pull request.\n\nI'd love to hear from you! Get in touch via X/Twitter [@krzyzanowskim](https://x.com/krzyzanowskim), Mastodon [@krzyzanowskim@mastodon.social](https://mastodon.social/@krzyzanowskim).\n\n## License\n\nBy using the Software, you accept the terms of the [License](LICENSE.md). The STTextView software is copyrighted by Marcin Krzyżanowski.\nIf use of the software under the [GPL v3.0](https://www.gnu.org/licenses/gpl-3.0.html) does not satisfy your organization’s legal department, [commercial licenses are available](https://krzyzanowskim.gumroad.com/l/sttextview). Feel free to contact for more details.\n","funding_links":["https://github.com/sponsors/krzyzanowskim"],"categories":["Swift","TextView"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrzyzanowskim%2FSTTextView","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkrzyzanowskim%2FSTTextView","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrzyzanowskim%2FSTTextView/lists"}