Skip to content

Commit 40d8e88

Browse files
thecoolwinteraustincondiff0xWDG
authored
Custom Text View (#211)
<!--- IMPORTANT: If this PR addresses multiple unrelated issues, it will be closed until separated. --> ### Description Replaces `STTextView` with a custom TextView implementation. Creates a new `TextView` and `TextViewController` classes that manage rendering text, handling text input, and keybinds. `TextViewController` replaces the `STTextViewController` class, connecting existing TextFormation classes, syntax highlighting, and other existing text view extensions. ### Related Issues * closes #208 * closes #195 * closes #184 * closes #57 ### Checklist TextView TODOs: - [X] load text - [X] render text - [X] scroll - [X] wrap text - [X] resize - [x] syntax highlighting - [x] cursor - [x] edit text - [x] isEditable - [x] Insert - [x] Delete - [x] Delete line - [x] Delete word - [x] Delete character - [x] Delete across lines - [x] Paste - [x] [Marked Text](https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TextEditing/TextEditing.html#//apple_ref/doc/uid/TP40009459-CH3-SW26) - [x] Line Numbers - [x] Select text - [x] Copy - [x] Multiple cursors - [x] Keyboard navigation - [x] Arrow keys - [x] Command & control arrow keys - [ ] Page up and down - [x] Tab widths & indents - [x] Live parameter updating - [x] Undo/redo - [x] Sync system appearance - [x] Highlight brackets - [x] TextFormation integration - [ ] ~MacOS Sonoma cursor~ Leaving for future PR. Will require rework of cursor view system. - [x] Update text from SwiftUI Binding (two-way binding) - [x] Accessibility - [x] Drag and Drop (bad, will need a rework but okay for now) -- - [x] I read and understood the [contributing guide](https://github.com/CodeEditApp/CodeEdit/blob/main/CONTRIBUTING.md) as well as the [code of conduct](https://github.com/CodeEditApp/CodeEdit/blob/main/CODE_OF_CONDUCT.md) - [x] The issues this PR addresses are related to each other - [x] My changes generate no new warnings - [x] My code builds and runs on my machine - [x] My changes are all related to the related issue above - [x] I documented my code ### Screenshots `// TODO` --------- Co-authored-by: Austin Condiff <[email protected]> Co-authored-by: Wesley de Groot <[email protected]>
1 parent cb7bfbf commit 40d8e88

File tree

90 files changed

+7290
-1017
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+7290
-1017
lines changed

.swiftpm/xcode/xcshareddata/xcschemes/CodeEditTextView.xcscheme

+10
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@
5252
ReferencedContainer = "container:">
5353
</BuildableReference>
5454
</TestableReference>
55+
<TestableReference
56+
skipped = "NO">
57+
<BuildableReference
58+
BuildableIdentifier = "primary"
59+
BlueprintIdentifier = "CodeEditInputViewTests"
60+
BuildableName = "CodeEditInputViewTests"
61+
BlueprintName = "CodeEditInputViewTests"
62+
ReferencedContainer = "container:">
63+
</BuildableReference>
64+
</TestableReference>
5565
</Testables>
5666
</TestAction>
5767
<LaunchAction

Package.resolved

+19-10
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,17 @@
55
"kind" : "remoteSourceControl",
66
"location" : "https://github.com/CodeEditApp/CodeEditLanguages.git",
77
"state" : {
8-
"revision" : "aa7d922b2aa783ae6f2a1a2cb7010ae62b700e17",
9-
"version" : "0.1.16"
8+
"revision" : "af29ab4a15474a0a38ef88ef65c20e58a0812e43",
9+
"version" : "0.1.17"
10+
}
11+
},
12+
{
13+
"identity" : "mainoffender",
14+
"kind" : "remoteSourceControl",
15+
"location" : "https://github.com/mattmassicotte/MainOffender",
16+
"state" : {
17+
"revision" : "343cc3797618c29b48b037b4e2beea0664e75315",
18+
"version" : "0.1.0"
1019
}
1120
},
1221
{
@@ -19,12 +28,12 @@
1928
}
2029
},
2130
{
22-
"identity" : "sttextview",
31+
"identity" : "swift-collections",
2332
"kind" : "remoteSourceControl",
24-
"location" : "https://github.com/krzyzanowskim/STTextView.git",
33+
"location" : "https://github.com/apple/swift-collections.git",
2534
"state" : {
26-
"branch" : "897c5ff",
27-
"revision" : "897c5ffe3c6b35664ab085d43238b3a95e79440d"
35+
"revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2",
36+
"version" : "1.0.4"
2837
}
2938
},
3039
{
@@ -50,17 +59,17 @@
5059
"kind" : "remoteSourceControl",
5160
"location" : "https://github.com/ChimeHQ/TextFormation",
5261
"state" : {
53-
"revision" : "158a603054ed5176f18d7c08ba355c0e05cb0586",
54-
"version" : "0.7.0"
62+
"revision" : "b4987856bc860643ac2c9cdbc7d5f3e9ade68377",
63+
"version" : "0.8.1"
5564
}
5665
},
5766
{
5867
"identity" : "textstory",
5968
"kind" : "remoteSourceControl",
6069
"location" : "https://github.com/ChimeHQ/TextStory",
6170
"state" : {
62-
"revision" : "b7b3fc551bd0177c32b3dc46d0478e9f0b6f8c6f",
63-
"version" : "0.7.2"
71+
"revision" : "8883fa739aa213e70e6cb109bfbf0a0b551e4cb5",
72+
"version" : "0.8.0"
6473
}
6574
}
6675
],

Package.swift

+51-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version: 5.7
1+
// swift-tools-version: 5.9
22
// The swift-tools-version declares the minimum version of Swift required to build this package.
33

44
import PackageDescription
@@ -7,48 +7,91 @@ let package = Package(
77
name: "CodeEditTextView",
88
platforms: [.macOS(.v13)],
99
products: [
10+
// A source editor with useful features for code editing.
1011
.library(
1112
name: "CodeEditTextView",
1213
targets: ["CodeEditTextView"]
1314
),
15+
// A Fast, Efficient text view for code.
16+
.library(
17+
name: "CodeEditInputView",
18+
targets: ["CodeEditInputView"]
19+
)
1420
],
1521
dependencies: [
16-
.package(
17-
url: "https://github.com/krzyzanowskim/STTextView.git",
18-
exact: "0.8.7"
19-
),
22+
// tree-sitter languages
2023
.package(
2124
url: "https://github.com/CodeEditApp/CodeEditLanguages.git",
2225
exact: "0.1.17"
2326
),
27+
// SwiftLint
2428
.package(
2529
url: "https://github.com/lukepistrol/SwiftLintPlugin",
2630
from: "0.2.2"
2731
),
32+
// Text mutation, storage helpers
33+
.package(
34+
url: "https://github.com/ChimeHQ/TextStory",
35+
from: "0.8.0"
36+
),
37+
// Rules for indentation, pair completion, whitespace
2838
.package(
2939
url: "https://github.com/ChimeHQ/TextFormation",
30-
from: "0.7.0"
40+
from: "0.8.1"
41+
),
42+
// Useful data structures
43+
.package(
44+
url: "https://github.com/apple/swift-collections.git",
45+
.upToNextMajor(from: "1.0.0")
3146
)
3247
],
3348
targets: [
49+
// A source editor with useful features for code editing.
3450
.target(
3551
name: "CodeEditTextView",
3652
dependencies: [
37-
"STTextView",
53+
"CodeEditInputView",
3854
"CodeEditLanguages",
3955
"TextFormation",
40-
.product(name: "STTextKitPlus", package: "STTextView")
4156
],
4257
plugins: [
4358
.plugin(name: "SwiftLint", package: "SwiftLintPlugin")
4459
]
4560
),
4661

62+
// The underlying text rendering view for CodeEditTextView
63+
.target(
64+
name: "CodeEditInputView",
65+
dependencies: [
66+
"TextStory",
67+
"TextFormation",
68+
.product(name: "Collections", package: "swift-collections")
69+
],
70+
plugins: [
71+
.plugin(name: "SwiftLint", package: "SwiftLintPlugin")
72+
]
73+
),
74+
75+
// Tests for the source editor
4776
.testTarget(
4877
name: "CodeEditTextViewTests",
4978
dependencies: [
5079
"CodeEditTextView",
5180
"CodeEditLanguages",
81+
],
82+
plugins: [
83+
.plugin(name: "SwiftLint", package: "SwiftLintPlugin")
84+
]
85+
),
86+
87+
// Tests for the input view
88+
.testTarget(
89+
name: "CodeEditInputViewTests",
90+
dependencies: [
91+
"CodeEditInputView",
92+
],
93+
plugins: [
94+
.plugin(name: "SwiftLint", package: "SwiftLintPlugin")
5295
]
5396
),
5497
]

README.md

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<p align="center">
2-
<img src="https://user-images.githubusercontent.com/806104/175655252-d77cef62-31f5-4f40-a2ad-c1406a6dd1b9.png" height="128">
2+
<img src="https://github.com/CodeEditApp/CodeEditTextView/assets/806104/b304b004-3bfc-413b-9db6-76d2af122acc" height="128">
33
<h1 align="center">CodeEditTextView</h1>
44
</p>
55

@@ -67,11 +67,10 @@ See this issue https://github.com/CodeEditApp/CodeEditLanguages/issues/10 on `Co
6767

6868
## Dependencies
6969

70-
Special thanks to both [Marcin Krzyzanowski](https://twitter.com/krzyzanowskim) & [Matt Massicotte](https://twitter.com/mattie) for the great work they've done!
70+
Special thanks to [Matt Massicotte](https://twitter.com/mattie) for the great work he's done!
7171

7272
| Package | Source | Author |
7373
| :- | :- | :- |
74-
| `STTextView` | [GitHub](https://github.com/krzyzanowskim/STTextView) | [Marcin Krzyzanowski](https://twitter.com/krzyzanowskim) |
7574
| `SwiftTreeSitter` | [GitHub](https://github.com/ChimeHQ/SwiftTreeSitter) | [Matt Massicotte](https://twitter.com/mattie) |
7675

7776
## License
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# ``CodeEditInputView``
2+
3+
A text editor designed to edit code documents.
4+
5+
## Overview
6+
7+
A text editor specialized for displaying and editing code documents. Features include basic text editing, extremely fast initial layout, support for handling large documents, customization options for code documents.
8+
9+
> This package contains a text view suitable for replacing `NSTextView` in some, ***specific*** cases. If you want a text view that can handle things like: left-to-right layout, custom layout elements, or feature parity with the system text view, consider using [STTextView](https://github.com/krzyzanowskim/STTextView) or [NSTextView](https://developer.apple.com/documentation/appkit/nstextview). The ``TextView`` exported by this library is designed to lay out documents made up of lines of text. However, it does not attempt to reason about the contents of the document. If you're looking to edit *source code* (indentation, syntax highlighting) consider using the parent library [CodeEditTextView](https://github.com/CodeEditApp/CodeEditTextView).
10+
11+
The ``TextView`` class is an `NSView` subclass that can be embedded in a scroll view or used standalone. It parses and renders lines of a document and handles mouse and keyboard events for text editing. It also renders styled strings for use cases like syntax highlighting.
12+
13+
## Topics
14+
15+
### Text View
16+
17+
- ``TextView``
18+
- ``CEUndoManager``
19+
20+
### Text Layout
21+
22+
- ``TextLayoutManager``
23+
- ``TextLine``
24+
- ``LineFragment``
25+
26+
### Text Selection
27+
28+
- ``TextSelectionManager``
29+
- ``TextSelectionManager/TextSelection``
30+
- ``CursorView``
31+
32+
### Supporting Types
33+
34+
- ``TextLineStorage``
35+
- ``HorizontalEdgeInsets``
36+
- ``LineEnding``
37+
- ``LineBreakStrategy``
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// NSRange+isEmpty.swift
3+
//
4+
//
5+
// Created by Khan Winter on 8/23/23.
6+
//
7+
8+
import Foundation
9+
10+
public extension NSRange {
11+
var isEmpty: Bool {
12+
length == 0
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// NSTextStorage+getLine.swift
3+
//
4+
//
5+
// Created by Khan Winter on 9/3/23.
6+
//
7+
8+
import AppKit
9+
10+
extension NSString {
11+
func getNextLine(startingAt location: Int) -> NSRange? {
12+
let range = NSRange(location: location, length: 0)
13+
var end: Int = NSNotFound
14+
var contentsEnd: Int = NSNotFound
15+
self.getLineStart(nil, end: &end, contentsEnd: &contentsEnd, for: range)
16+
if end != NSNotFound && contentsEnd != NSNotFound && end != contentsEnd {
17+
return NSRange(location: contentsEnd, length: end - contentsEnd)
18+
} else {
19+
return nil
20+
}
21+
}
22+
}
23+
24+
extension NSTextStorage {
25+
func getNextLine(startingAt location: Int) -> NSRange? {
26+
(self.string as NSString).getNextLine(startingAt: location)
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// PixelAligned.swift
3+
//
4+
//
5+
// Created by Khan Winter on 9/10/23.
6+
//
7+
8+
import Foundation
9+
10+
public extension NSRect {
11+
/// Creates a rect pixel-aligned on all edges.
12+
var pixelAligned: NSRect {
13+
NSIntegralRectWithOptions(self, .alignAllEdgesNearest)
14+
}
15+
}
16+
17+
public extension NSPoint {
18+
/// Creates a point that's pixel-aligned.
19+
var pixelAligned: NSPoint {
20+
NSIntegralRectWithOptions(NSRect(x: self.x, y: self.y, width: 0, height: 0), .alignAllEdgesNearest).origin
21+
}
22+
}

0 commit comments

Comments
 (0)