Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Text View #211

Merged
merged 79 commits into from
Dec 9, 2023
Merged
Show file tree
Hide file tree
Changes from 78 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
f770d0f
Begin TextView implementation
thecoolwinter Jun 29, 2023
f70e24d
Add height to textline tree
thecoolwinter Jul 15, 2023
dbfe605
Merge branch 'main' into feat/inputview
thecoolwinter Jul 15, 2023
2776c2e
Get something rendering...
thecoolwinter Jul 16, 2023
ab76fef
Wrap lines, remove custom layer
thecoolwinter Jul 17, 2023
864400c
Begin integrating highlighting system
thecoolwinter Jul 17, 2023
6069382
Syntax highlighting integration (no edit)
thecoolwinter Jul 17, 2023
f3d8a5a
Update TODO.md
thecoolwinter Jul 17, 2023
d4780de
Begin layer reuse instead of raw draw
thecoolwinter Jul 22, 2023
6678dc4
Use reusable views for rendering lines
thecoolwinter Aug 16, 2023
a685b9f
Initial cursor implementation
thecoolwinter Aug 16, 2023
2629ec2
Fix some resizing bugs
thecoolwinter Aug 16, 2023
b45ec3d
Begin (completely non functional) text input
thecoolwinter Aug 16, 2023
2a292f0
Refactor TextLineStorage, Add Insert Editing
thecoolwinter Aug 20, 2023
a40000c
Final layout refactor, resizable, copy/paste
thecoolwinter Aug 22, 2023
c3c9e62
Fix scroll jumping on scroll up
thecoolwinter Aug 22, 2023
c37ab5c
Add setNeedsLayout to TextLayoutManager
thecoolwinter Aug 22, 2023
417bd32
Move line storage builder
thecoolwinter Aug 22, 2023
f5cc9f6
Add line number to Line Storage nodes
thecoolwinter Aug 22, 2023
fbe25d8
Add GutterView
thecoolwinter Aug 22, 2023
19500f9
Line Numbers final, fix insert bug
thecoolwinter Aug 23, 2023
1f16841
Selected line highlights, remove strong references
thecoolwinter Aug 23, 2023
eee21f9
Fix Cursor + Selection Background Misalignment
thecoolwinter Aug 23, 2023
8452403
Fix text position calculation
thecoolwinter Aug 23, 2023
edbe8d3
Spelling Error
thecoolwinter Aug 23, 2023
5a7bbcd
Begin RB delete, add docs
thecoolwinter Aug 24, 2023
a757f5c
Begin Delete
thecoolwinter Aug 25, 2023
30a6222
Add TextSelectionManager as NSTextStorage delegate
thecoolwinter Aug 26, 2023
793bf10
Small refactor
thecoolwinter Aug 26, 2023
377c986
Fix TODO, Fix Performance Test
thecoolwinter Aug 26, 2023
8d877cc
Add RB Tree Delete, lazy update cursors
thecoolwinter Aug 31, 2023
a5f077e
Add small test
thecoolwinter Aug 31, 2023
63cff48
Begin `TextLayoutManager` updates for editing
thecoolwinter Sep 3, 2023
203ffca
Add CodeEditTextInput and Common targets
thecoolwinter Sep 3, 2023
3141026
Finish delete
thecoolwinter Sep 3, 2023
2baa6d9
Merge remote-tracking branch 'origin/main' into feat/inputview
thecoolwinter Sep 3, 2023
bdc2d6d
Finalize Insert, Delete, Newlines, Delete Lines
thecoolwinter Sep 11, 2023
1c8aea5
Arrow Left & Right, Fix Lint Errors, Trailing Insets
thecoolwinter Sep 11, 2023
b0ac245
Fix Lint Bugs, Remove Warnings
thecoolwinter Sep 11, 2023
a3aad97
Add Some TextSelectionManager Tests
thecoolwinter Sep 12, 2023
1e42a4c
Add `scrollSelectionToVisible`, Work on Arrow Keys
thecoolwinter Sep 13, 2023
882fa43
Finish Keyboard Navigation
thecoolwinter Sep 15, 2023
18a958c
QOL GutterView Improvements
thecoolwinter Sep 17, 2023
0a00de9
Add line break strategies
thecoolwinter Sep 19, 2023
37a8331
Limit word break lookback to startingOffset
thecoolwinter Sep 19, 2023
307b0b3
Draw Selection Rects
thecoolwinter Sep 19, 2023
f22ae5a
Drag To Select, Autoscroll On Drag
thecoolwinter Oct 2, 2023
7046569
Finalize Selection Modification API
thecoolwinter Oct 6, 2023
668b294
Fix Gutter Clipping, Improve Insert Performance
thecoolwinter Oct 7, 2023
048e7b8
Fix TextLineStorage.update Test
thecoolwinter Oct 7, 2023
5c389da
Fix Undo/Redo, Line Fragment Move, Lint Errors
thecoolwinter Oct 7, 2023
d7050ab
Typing Attributes, Remove STTextView, Integrate Previous Features
thecoolwinter Oct 14, 2023
9e6a7b5
Remove Print Statement
thecoolwinter Oct 14, 2023
61a1b9b
Accessibility
thecoolwinter Oct 14, 2023
181e7ff
Fix Linter
thecoolwinter Oct 14, 2023
5a11ada
Fix Editing Bugs, Pass Tests
thecoolwinter Oct 15, 2023
5384b6b
Fix Basic Editing At End Of Files & New Files
thecoolwinter Oct 17, 2023
f7760af
Fix Empty File, End Of File, LineEndings
thecoolwinter Oct 19, 2023
9586155
Fix TextFormation, EOD Selection, Layout Transactions
thecoolwinter Oct 20, 2023
5bf20ed
Fix textOffsetAtPoint, Add Selection getFillRects
thecoolwinter Oct 21, 2023
d6f399c
Remove Xcode Baselines
thecoolwinter Oct 21, 2023
bdcd0f6
Multi-Cursor, Sync Cursor Animations, Line Highlights
thecoolwinter Oct 22, 2023
af81a47
Fix Lint Errors
thecoolwinter Oct 22, 2023
4d7dbc5
Merge remote-tracking branch 'upstream' into feat/inputview
thecoolwinter Oct 22, 2023
38db0d4
Fix Lint Errors
thecoolwinter Oct 22, 2023
dae9e60
Misc fixes, Fixup SwiftUI API
thecoolwinter Oct 24, 2023
8a221c4
Fix Tests
thecoolwinter Oct 24, 2023
9e0ca63
Correctly Reset `setTextStorage`, background colors
thecoolwinter Nov 2, 2023
f0e6059
Initial Marked Text implementation
thecoolwinter Nov 7, 2023
4716991
Update README.md Icon
austincondiff Nov 8, 2023
05390c5
Update README.md
austincondiff Nov 8, 2023
93bfd76
Marked Text, Remove Common Module, `lineBreakStrategy`
thecoolwinter Nov 9, 2023
81c7f98
Update SwiftUI API, Add `CursorPosition` & `TextViewCoordinator`
thecoolwinter Nov 18, 2023
0016b1d
Merge remote-tracking branch 'upstream' into feat/inputview
thecoolwinter Nov 18, 2023
7cf6937
Remove `internal`, Unnecessary Public Extensions
thecoolwinter Nov 18, 2023
7ca798d
Update TextViewControllerTests.swift
thecoolwinter Nov 18, 2023
2b6ae13
Explicit frame size in test
thecoolwinter Nov 18, 2023
22dfab2
Linter
thecoolwinter Nov 18, 2023
afcafa9
Update Sources/CodeEditInputView/TextView/TextView+Move.swift
austincondiff Dec 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/CodeEditTextView.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CodeEditInputViewTests"
BuildableName = "CodeEditInputViewTests"
BlueprintName = "CodeEditInputViewTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
Expand Down
29 changes: 19 additions & 10 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/CodeEditApp/CodeEditLanguages.git",
"state" : {
"revision" : "aa7d922b2aa783ae6f2a1a2cb7010ae62b700e17",
"version" : "0.1.16"
"revision" : "af29ab4a15474a0a38ef88ef65c20e58a0812e43",
"version" : "0.1.17"
}
},
{
"identity" : "mainoffender",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mattmassicotte/MainOffender",
"state" : {
"revision" : "343cc3797618c29b48b037b4e2beea0664e75315",
"version" : "0.1.0"
}
},
{
Expand All @@ -19,12 +28,12 @@
}
},
{
"identity" : "sttextview",
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/krzyzanowskim/STTextView.git",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"branch" : "897c5ff",
"revision" : "897c5ffe3c6b35664ab085d43238b3a95e79440d"
"revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2",
"version" : "1.0.4"
}
},
{
Expand All @@ -50,17 +59,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/ChimeHQ/TextFormation",
"state" : {
"revision" : "158a603054ed5176f18d7c08ba355c0e05cb0586",
"version" : "0.7.0"
"revision" : "b4987856bc860643ac2c9cdbc7d5f3e9ade68377",
"version" : "0.8.1"
}
},
{
"identity" : "textstory",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ChimeHQ/TextStory",
"state" : {
"revision" : "b7b3fc551bd0177c32b3dc46d0478e9f0b6f8c6f",
"version" : "0.7.2"
"revision" : "8883fa739aa213e70e6cb109bfbf0a0b551e4cb5",
"version" : "0.8.0"
}
}
],
Expand Down
59 changes: 51 additions & 8 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.7
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand All @@ -7,48 +7,91 @@ let package = Package(
name: "CodeEditTextView",
platforms: [.macOS(.v13)],
products: [
// A source editor with useful features for code editing.
.library(
name: "CodeEditTextView",
targets: ["CodeEditTextView"]
),
// A Fast, Efficient text view for code.
.library(
name: "CodeEditInputView",
targets: ["CodeEditInputView"]
)
],
dependencies: [
.package(
url: "https://github.com/krzyzanowskim/STTextView.git",
exact: "0.8.7"
),
// tree-sitter languages
.package(
url: "https://github.com/CodeEditApp/CodeEditLanguages.git",
exact: "0.1.17"
),
// SwiftLint
.package(
url: "https://github.com/lukepistrol/SwiftLintPlugin",
from: "0.2.2"
),
// Text mutation, storage helpers
.package(
url: "https://github.com/ChimeHQ/TextStory",
from: "0.8.0"
),
// Rules for indentation, pair completion, whitespace
.package(
url: "https://github.com/ChimeHQ/TextFormation",
from: "0.7.0"
from: "0.8.1"
),
// Useful data structures
.package(
url: "https://github.com/apple/swift-collections.git",
.upToNextMajor(from: "1.0.0")
)
],
targets: [
// A source editor with useful features for code editing.
.target(
name: "CodeEditTextView",
dependencies: [
"STTextView",
"CodeEditInputView",
"CodeEditLanguages",
"TextFormation",
.product(name: "STTextKitPlus", package: "STTextView")
],
plugins: [
.plugin(name: "SwiftLint", package: "SwiftLintPlugin")
]
),

// The underlying text rendering view for CodeEditTextView
.target(
name: "CodeEditInputView",
dependencies: [
"TextStory",
"TextFormation",
.product(name: "Collections", package: "swift-collections")
],
plugins: [
.plugin(name: "SwiftLint", package: "SwiftLintPlugin")
]
),

// Tests for the source editor
.testTarget(
name: "CodeEditTextViewTests",
dependencies: [
"CodeEditTextView",
"CodeEditLanguages",
],
plugins: [
.plugin(name: "SwiftLint", package: "SwiftLintPlugin")
]
),

// Tests for the input view
.testTarget(
name: "CodeEditInputViewTests",
dependencies: [
"CodeEditInputView",
],
plugins: [
.plugin(name: "SwiftLint", package: "SwiftLintPlugin")
]
),
]
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<p align="center">
<img src="https://user-images.githubusercontent.com/806104/175655252-d77cef62-31f5-4f40-a2ad-c1406a6dd1b9.png" height="128">
<img src="https://github.com/CodeEditApp/CodeEditTextView/assets/806104/b304b004-3bfc-413b-9db6-76d2af122acc" height="128">
<h1 align="center">CodeEditTextView</h1>
</p>

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

## Dependencies

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

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

## License
Expand Down
37 changes: 37 additions & 0 deletions Sources/CodeEditInputView/Documentation.docc/Documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# ``CodeEditInputView``

A text editor designed to edit code documents.

## Overview

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.

> 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).

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.

## Topics

### Text View

- ``TextView``
- ``CEUndoManager``

### Text Layout

- ``TextLayoutManager``
- ``TextLine``
- ``LineFragment``

### Text Selection

- ``TextSelectionManager``
- ``TextSelectionManager/TextSelection``
- ``CursorView``

### Supporting Types

- ``TextLineStorage``
- ``HorizontalEdgeInsets``
- ``LineEnding``
- ``LineBreakStrategy``
14 changes: 14 additions & 0 deletions Sources/CodeEditInputView/Extensions/NSRange+isEmpty.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// NSRange+isEmpty.swift
//
//
// Created by Khan Winter on 8/23/23.
//

import Foundation

public extension NSRange {
var isEmpty: Bool {
length == 0
}
}
28 changes: 28 additions & 0 deletions Sources/CodeEditInputView/Extensions/NSTextStorage+getLine.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// NSTextStorage+getLine.swift
//
//
// Created by Khan Winter on 9/3/23.
//

import AppKit

extension NSString {
func getNextLine(startingAt location: Int) -> NSRange? {
let range = NSRange(location: location, length: 0)
var end: Int = NSNotFound
var contentsEnd: Int = NSNotFound
self.getLineStart(nil, end: &end, contentsEnd: &contentsEnd, for: range)
if end != NSNotFound && contentsEnd != NSNotFound && end != contentsEnd {
return NSRange(location: contentsEnd, length: end - contentsEnd)
} else {
return nil
}
}
}

extension NSTextStorage {
func getNextLine(startingAt location: Int) -> NSRange? {
(self.string as NSString).getNextLine(startingAt: location)
}
}
22 changes: 22 additions & 0 deletions Sources/CodeEditInputView/Extensions/PixelAligned.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// PixelAligned.swift
//
//
// Created by Khan Winter on 9/10/23.
//

import Foundation

public extension NSRect {
/// Creates a rect pixel-aligned on all edges.
var pixelAligned: NSRect {
NSIntegralRectWithOptions(self, .alignAllEdgesNearest)
}
}

public extension NSPoint {
/// Creates a point that's pixel-aligned.
var pixelAligned: NSPoint {
NSIntegralRectWithOptions(NSRect(x: self.x, y: self.y, width: 0, height: 0), .alignAllEdgesNearest).origin
}
}
Loading