Skip to content

Commit

Permalink
Custom Text View (#211)
Browse files Browse the repository at this point in the history
<!--- 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 <austin.condiff@gmail.com>
Co-authored-by: Wesley de Groot <email@wesleydegroot.nl>
  • Loading branch information
3 people committed Dec 9, 2023
1 parent cb7bfbf commit 40d8e88
Show file tree
Hide file tree
Showing 90 changed files with 7,290 additions and 1,017 deletions.
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

0 comments on commit 40d8e88

Please sign in to comment.