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

RunestoneSwiftUI #1

Draft
wants to merge 58 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
050899e
WIP on RunestoneSwiftUI.TextEditor
nighthawk May 23, 2022
887de36
Refactoring, prepare to override theme SwiftUI-style
nighthawk May 24, 2022
8d3a0ed
Add `.themeFontSize` modifier
nighthawk Jun 2, 2022
2ef6d5d
Move Configuration to TextEditor so that it gets documentation
nighthawk Jun 4, 2022
d4c70c0
Apply missing colours
nighthawk Jun 5, 2022
aa28f8d
Add coordinator
nighthawk Jun 18, 2022
8474c82
Merge branch 'main' into swiftui
nighthawk Jun 18, 2022
715ac4e
Merge branch 'main' into swiftui
nighthawk Jul 10, 2022
2205e02
Merge branch 'main' into swiftui
nighthawk Aug 24, 2022
8a6a575
Adds LineControllerStorage
simonbs Aug 26, 2022
a55d7ab
Merge branch 'main' into enhancement/line-controller-storage
simonbs Aug 26, 2022
5b66923
Fixes SwiftLint warnings
simonbs Aug 26, 2022
caab74f
Decreases width of caret
simonbs Aug 27, 2022
e97e497
Merge branch 'enhancement/decrease-caret-width' into enhancement/line…
simonbs Aug 27, 2022
e403b8b
Merge branch 'main' into enhancement/line-controller-storage
simonbs Aug 27, 2022
391332e
Places the caret correctly when navigating between lines
simonbs Aug 27, 2022
a39342d
Merge branch 'enhancement/line-controller-storage' into enhancement/l…
simonbs Aug 27, 2022
45b387c
Merge branch 'bugfix/up-down-arrow-key-navigation' into enhancement/l…
simonbs Aug 27, 2022
bef2aa8
Merge branch 'main' into enhancement/line-movement
simonbs Aug 27, 2022
1550393
Removes unused function
simonbs Aug 27, 2022
ab8813b
Removes mapping of direction
simonbs Aug 27, 2022
ecbe444
Improves code formatting
simonbs Aug 27, 2022
6edb05f
Places caret at front of next line fragment when caret is behind last…
simonbs Aug 27, 2022
5cd3e28
Treats line fragments as line endings
simonbs Aug 27, 2022
40b491d
Calls caretRect(at:) on layoutManager instead of self
simonbs Aug 27, 2022
78468cb
Renames shouldNotifyInputDelegateAboutSelectionChangeInLayoutSubviews
simonbs Aug 27, 2022
208f551
Renames shouldPreserveUndoStackWhenSettingString
simonbs Aug 27, 2022
18106b7
Improves line movement
simonbs Aug 27, 2022
6f85d51
Merge branch 'main' into enhancement/line-movement
simonbs Aug 27, 2022
b0fcea5
Fixes incorrect selection
simonbs Aug 27, 2022
3ffb871
Merge branch 'swiftui' into swiftui+line-movement
nighthawk Aug 28, 2022
c782725
Differentiates between line and paragraph
simonbs Aug 28, 2022
36f5d4f
Removes Ctrl+A and Ctrl+E key commands
simonbs Aug 28, 2022
1e76fad
Removes special handling of caret on next line fragment when otherwis…
simonbs Aug 28, 2022
b77f951
Supports platform-specific behavior for keyboard navigation
simonbs Aug 28, 2022
2f85ef6
Fixes SwiftLint warnings
simonbs Aug 28, 2022
289d0e0
Merge branch 'main' into enhancement/line-movement
simonbs Aug 28, 2022
b1fb915
Merge branch 'enhancement/line-movement' into swiftui+line-movement
nighthawk Aug 29, 2022
07cf177
Introduces shouldMoveCaretToNextLineFragment(forLocation:in:)
simonbs Aug 29, 2022
b2842ff
Merge branch 'main' into enhancement/line-movement
simonbs Aug 30, 2022
f015f89
Merge branch 'enhancement/line-movement' into swiftui+line-movement
nighthawk Aug 30, 2022
3f9b7cf
Merge branch 'main' into swiftui
nighthawk Aug 31, 2022
96b4566
Merge branch 'swiftui' into swiftui+line-movement
nighthawk Sep 2, 2022
72ac52f
Merge branch 'main' into swiftui
nighthawk Sep 4, 2022
7edd66a
Merge branch 'main' into swiftui
nighthawk Sep 23, 2022
674e62a
Workaround for runtime crash
nighthawk Apr 20, 2023
912ce81
Merge branch 'main' into swiftui
nighthawk Apr 20, 2023
d32d4ce
Update TextEditor for text coming *from* binding, too
nighthawk Apr 20, 2023
00bd8b5
Re-instate line that shouldn't have been deleted, and maintain correc…
nighthawk Apr 29, 2023
a9e2ff0
Vision OS compile fixes (#3)
nighthawk Dec 2, 2023
364a0f4
Merging main into SwiftUI branch
nighthawk Apr 3, 2024
49cdb8c
Merge branch 'main' into main+swiftui
nighthawk Apr 3, 2024
0f6dedb
Merge pull request #4 from maparoni/main+swiftui
nighthawk Apr 3, 2024
48fac3d
Revert a change
nighthawk Apr 3, 2024
36d91bf
Add a lock around parse, to prevent concurrent access to the underlyi…
migueldeicaza Apr 15, 2024
81e23a9
Allow TextLocation to be created (#372)
migueldeicaza May 13, 2024
1fad339
Fix build with Xcode 16 (#375)
eliperkins Jun 10, 2024
2fa9f3d
Merge remote-tracking branch 'upstream/main' into swiftui
nighthawk Jun 18, 2024
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
4 changes: 3 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ let package = Package(
.iOS(.v14)
],
products: [
.library(name: "Runestone", targets: ["Runestone"])
.library(name: "Runestone", targets: ["Runestone"]),
.library(name: "RunestoneSwiftUI", targets: ["RunestoneSwiftUI"])
],
dependencies: [
.package(url: "https://github.com/tree-sitter/tree-sitter", .upToNextMinor(from: "0.20.9"))
Expand All @@ -22,6 +23,7 @@ let package = Package(
.copy("PrivacyInfo.xcprivacy"),
.process("TextView/Appearance/Theme.xcassets")
]),
.target(name: "RunestoneSwiftUI", dependencies: ["Runestone"]),
.target(name: "TestTreeSitterLanguages", cSettings: [
.unsafeFlags(["-w"])
]),
Expand Down
2 changes: 1 addition & 1 deletion Sources/Runestone/Library/UITextInput+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import UIKit
extension UITextInput where Self: NSObject {
var sbs_textSelectionDisplayInteraction: UITextSelectionDisplayInteraction? {
let interactionAssistantKey = "int" + "ssAnoitcare".reversed() + "istant"
let selectionViewManagerKey = "les_".reversed() + "ection" + "reganaMweiV".reversed()
let selectionViewManagerKey: String = "les_".reversed() + "ection" + "reganaMweiV".reversed()
guard responds(to: Selector(interactionAssistantKey)) else {
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Runestone/TextView/Appearance/Theme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public extension Theme {
var pageGuideHairlineWidth: CGFloat {
hairlineLength
}

var markedTextBackgroundCornerRadius: CGFloat {
0
}
Expand Down
6 changes: 6 additions & 0 deletions Sources/Runestone/TextView/Navigation/TextLocation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ public struct TextLocation: Hashable, Equatable {
/// Column in the line.
public let column: Int

/// Initializes TextLocation from the given line and column
public init (lineNumber: Int, column: Int) {
self.lineNumber = lineNumber
self.column = column
}

init(_ linePosition: LinePosition) {
self.lineNumber = linePosition.row
self.column = linePosition.column
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ final class TreeSitterInternalLanguageMode: InternalLanguageMode {
private let lineManager: LineManager
private let rootLanguageLayer: TreeSitterLanguageLayer
private let operationQueue = OperationQueue()
private let parseLock = NSLock()

init(language: TreeSitterInternalLanguage, languageProvider: TreeSitterLanguageProvider?, stringView: StringView, lineManager: LineManager) {
self.stringView = stringView
Expand All @@ -37,7 +38,9 @@ final class TreeSitterInternalLanguageMode: InternalLanguageMode {
}

func parse(_ text: NSString) {
rootLanguageLayer.parse(text)
parseLock.withLock {
rootLanguageLayer.parse(text)
}
}

func parse(_ text: NSString, completion: @escaping ((Bool) -> Void)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ extension TreeSitterLanguageLayer {
if let oldTree = oldTree, let newTree = tree {
let changedRanges = oldTree.rangesChanged(comparingTo: newTree)
for changedRange in changedRanges {
let startRow = Int(changedRange.startPoint.row)
let endRow = Int(changedRange.endPoint.row)
let startRow = Int(min(changedRange.startPoint.row, changedRange.endPoint.row))
let endRow = Int(max(changedRange.startPoint.row, changedRange.endPoint.row))
for row in startRow ... endRow {
let line = lineManager.line(atRow: row)
lineChangeSet.markLineEdited(line)
Expand Down
38 changes: 38 additions & 0 deletions Sources/RunestoneSwiftUI/OverridingTheme.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// OverridingTheme.swift
//
//
// Created by Adrian Schönig on 23/5/2022.
//

import UIKit
import Runestone

Check failure on line 9 in Sources/RunestoneSwiftUI/OverridingTheme.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Sorted Imports Violation: Imports should be sorted (sorted_imports)

class OverridingTheme: Theme {
let base: Theme

Check failure on line 13 in Sources/RunestoneSwiftUI/OverridingTheme.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
init(base: Theme) {
self.base = base
self.font = base.font
self.textColor = base.textColor
}

Check failure on line 19 in Sources/RunestoneSwiftUI/OverridingTheme.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
var font: UIFont
var textColor: UIColor
var gutterBackgroundColor: UIColor { base.gutterBackgroundColor }
var gutterHairlineColor: UIColor { base.gutterHairlineColor }
var lineNumberColor: UIColor { base.lineNumberColor }
var lineNumberFont: UIFont { base.lineNumberFont }
var selectedLineBackgroundColor: UIColor { base.selectedLineBackgroundColor }
var selectedLinesLineNumberColor: UIColor { base.selectedLinesLineNumberColor }
var selectedLinesGutterBackgroundColor: UIColor { base.selectedLinesGutterBackgroundColor }
var invisibleCharactersColor: UIColor { base.invisibleCharactersColor }
var pageGuideHairlineColor: UIColor { base.pageGuideHairlineColor }
var pageGuideBackgroundColor: UIColor { base.pageGuideBackgroundColor }
var markedTextBackgroundColor: UIColor { base.markedTextBackgroundColor }

Check failure on line 33 in Sources/RunestoneSwiftUI/OverridingTheme.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
func textColor(for highlightName: String) -> UIColor? {
base.textColor(for: highlightName)
}

Check failure on line 37 in Sources/RunestoneSwiftUI/OverridingTheme.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
}
131 changes: 131 additions & 0 deletions Sources/RunestoneSwiftUI/TextEditor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//
// TextEditor.swift
//
//
// Created by Adrian Schönig on 23/5/2022.
//

import SwiftUI
import UIKit

@_exported import Runestone

public struct TextEditor: UIViewRepresentable {

@Environment(\.themeFontSize) var themeFontSize

@Binding var text: String

let actualTheme: OverridingTheme
public var theme: Theme { actualTheme.base }
public let language: TreeSitterLanguage?
public let configuration: Configuration

public init(text: Binding<String>, theme: Theme, language: TreeSitterLanguage? = nil, configuration: Configuration = .init()) {
self._text = text
self.actualTheme = OverridingTheme(base: theme)
self.language = language
self.configuration = configuration
}

public func makeCoordinator() -> TextEditorCoordinator {
.init()
}

public func makeUIView(context: Context) -> UIView {
let textView = TextView()
textView.apply(configuration)

textView.editorDelegate = context.coordinator
context.coordinator.configure(text: $text, theme: actualTheme, language: language) { state in
textView.setState(state)
}

// We assume your theme matches the device's mode
textView.backgroundColor = .systemBackground

textView.insertionPointColor = theme.textColor
textView.selectionBarColor = theme.textColor
textView.selectionHighlightColor = theme.textColor.withAlphaComponent(0.2)

return textView
}

public func updateUIView(_ uiView: UIView, context: Context) {
guard let textView = uiView as? TextView else { return assertionFailure() }

Check failure on line 55 in Sources/RunestoneSwiftUI/TextEditor.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Conditional Returns on Newline Violation: Conditional statements should always return on the next line (conditional_returns_on_newline)

// Update from context, such as...
switch context.environment.disableAutocorrection {
case .none: textView.autocorrectionType = .default

Check failure on line 59 in Sources/RunestoneSwiftUI/TextEditor.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Cases on Newline Violation: Cases inside a switch should always be on a newline (switch_case_on_newline)
case .some(false): textView.autocorrectionType = .yes

Check failure on line 60 in Sources/RunestoneSwiftUI/TextEditor.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Cases on Newline Violation: Cases inside a switch should always be on a newline (switch_case_on_newline)
case .some(true): textView.autocorrectionType = .no

Check failure on line 61 in Sources/RunestoneSwiftUI/TextEditor.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Cases on Newline Violation: Cases inside a switch should always be on a newline (switch_case_on_newline)
}

if let fontSize = themeFontSize, fontSize != actualTheme.font.pointSize {
actualTheme.font = UIFont(descriptor: theme.font.fontDescriptor, size: fontSize)
textView.theme = actualTheme
}

context.coordinator.configure(text: $text, theme: actualTheme, language: language) { state in
textView.setState(state)
}
}
}

extension TextEditor {
public init(text: String, theme: Theme, language: TreeSitterLanguage? = nil, configuration: Configuration = .init()) {
var config = configuration
config.isEditable = false
self.init(text: .constant(text), theme: theme, language: language, configuration: config)
}
}

public class TextEditorCoordinator: ObservableObject {
var text: Binding<String>?

func configure(text: Binding<String>, theme: Theme, language: TreeSitterLanguage?, completion: @escaping (TextViewState) -> Void) {
guard self.text?.wrappedValue != text.wrappedValue else { return }

Check failure on line 87 in Sources/RunestoneSwiftUI/TextEditor.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Conditional Returns on Newline Violation: Conditional statements should always return on the next line (conditional_returns_on_newline)

self.text = text

DispatchQueue.global(qos: .userInteractive).async {
let state: TextViewState
if let language = language {
state = TextViewState(text: text.wrappedValue, theme: theme, language: language)
} else {
state = TextViewState(text: text.wrappedValue, theme: theme)
}
DispatchQueue.main.async {
completion(state)
}
}
}
}

extension TextEditorCoordinator: Runestone.TextViewDelegate {
public func textViewDidChange(_ textView: TextView) {
text?.wrappedValue = textView.text
}
}

// MARK: .themeFontSize

public struct ThemeFontSizeKey: EnvironmentKey {
public static let defaultValue: Double? = nil
}

extension EnvironmentValues {
public var themeFontSize: Double? {
get { self[ThemeFontSizeKey.self] }
set { self[ThemeFontSizeKey.self] = newValue }
}
}

extension View {

/// Overrides the font size of the `RunestoneUI.TextEditor`'s theme
/// - Parameter size: Text size in points
public func themeFontSize(_ size: Double) -> some View {
environment(\.themeFontSize, size)
}
}
35 changes: 35 additions & 0 deletions Sources/RunestoneSwiftUI/TextView+Configuration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// TextView+Configuration.swift
//
//
// Created by Adrian Schönig on 23/5/2022.
//

import UIKit

@_exported import Runestone

extension TextEditor {

/// Configuration options of the TextEditor
public struct Configuration {
public init(isEditable: Bool = true, showLineNumbers: Bool = false) {
self.isEditable = isEditable
self.showLineNumbers = showLineNumbers
}

/// A Boolean value that indicates whether the text view is editable.
public var isEditable: Bool = true

/// Enable to show line numbers in the gutter.
public var showLineNumbers: Bool = false
}
}


extension TextView {
func apply(_ configuration: TextEditor.Configuration) {
showLineNumbers = configuration.showLineNumbers
isEditable = configuration.isEditable
}
}
Loading