Skip to content

Commit 30b6ac9

Browse files
committed
Initial drop
... will it build?
1 parent 2ea870f commit 30b6ac9

10 files changed

Lines changed: 893 additions & 2 deletions

File tree

.github/workflows/swift.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Build and Test
2+
3+
on:
4+
push:
5+
pull_request:
6+
schedule:
7+
- cron: "35 10 * * 2"
8+
9+
jobs:
10+
nextstep:
11+
runs-on: macos-latest
12+
steps:
13+
- name: Select latest available Xcode
14+
uses: maxim-lobanov/setup-xcode@v1.2.1
15+
with:
16+
xcode-version: 12.2
17+
- name: Checkout Repository
18+
uses: actions/checkout@v2
19+
- name: Build Swift Debug Package
20+
run: swift build -c debug
21+
- name: Build Swift Release Package
22+
run: swift build -c release

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,6 @@ fastlane/test_output
8888
# https://github.com/johnno1962/injectionforxcode
8989

9090
iOSInjectionProject/
91+
92+
Package.resolved
93+

Package.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// swift-tools-version:5.3
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
7+
name: "CodeEditor",
8+
9+
platforms: [
10+
.macOS(.v10_15), .iOS(.v13)
11+
],
12+
13+
products: [
14+
.library(name: "CodeEditor", targets: [ "CodeEditor" ])
15+
],
16+
17+
dependencies: [
18+
.package(url: "https://github.com/raspu/Highlightr", from: "2.1.2")
19+
],
20+
21+
targets: [
22+
.target(name: "CodeEditor", dependencies: [ "Highlightr" ])
23+
]
24+
)

README.md

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,119 @@
1-
# CodeEditor
2-
A SwiftUI TextEditor with syntax highlighting using Highlight.js
1+
<h2>
2+
CodeEditor
3+
<img src="https://zeezide.de/img/svgshaper/SVGShaper512.png"
4+
align="right" width="128" height="128" />
5+
</h2>
6+
7+
![SwiftUI](https://img.shields.io/badge/SwiftUI-orange.svg)
8+
![Swift5.3](https://img.shields.io/badge/swift-5.3-blue.svg)
9+
![macOS](https://img.shields.io/badge/os-macOS-green.svg?style=flat)
10+
![iOS](https://img.shields.io/badge/os-iOS-green.svg?style=flat)
11+
[![Build and Test](https://github.com/ZeeZide/CodeEditor/actions/workflows/swift.yml/badge.svg?branch=main)](https://github.com/ZeeZide/CodeEditor/actions/workflows/swift.yml)
12+
13+
A [SwiftUI](https://developer.apple.com/xcode/swiftui/)
14+
TextEditor View with syntax highlighting using
15+
[Highlight.js](https://highlightjs.org).
16+
17+
It builds on top of
18+
[Highlightr](https://github.com/raspu/Highlightr)
19+
which does the wrapping of Highlight.js.
20+
CodeEditor then packages things up for SwiftUI.
21+
22+
Example usage in
23+
[SVG Shaper for SwiftUI](https://zeezide.de/en/products/svgshaper/)
24+
(used for editing
25+
![SVG Shaper Screenshot](https://pbs.twimg.com/media/E0ydNH9XEAQ-USY?format=png)
26+
(Shaper is not using Highlightr, but is otherwise quite similar).
27+
28+
Highlightr example:
29+
![Highlight Example](https://raw.githubusercontent.com/raspu/Highlightr/master/coding.gif)
30+
31+
32+
## Usage
33+
34+
### Adding the Package
35+
36+
The Swift package URL is: `https://github.com/ZeeZide/CodeEditor.git`
37+
38+
### Using it in a SwiftUI App
39+
40+
To use the code editor as a Viewer, simply pass the source code
41+
```swift
42+
struct ContentView: View {
43+
44+
var body: some View {
45+
CodeEditor(source: "let a = 42")
46+
}
47+
}
48+
```
49+
50+
If it should act as an actual editor, pass it `Binding`:
51+
52+
```swift
53+
struct ContentView: View {
54+
55+
@State private var source = "let a = 42\n"
56+
57+
var body: some View {
58+
CodeEditor(source: $source, language: .swift, theme: .ocean)
59+
}
60+
}
61+
```
62+
63+
### Languages and Themes
64+
65+
Highlight.js supports more than 180 languages and over 80 different themes.
66+
67+
The available languages and themes can be accessed using:
68+
```swift
69+
CodeEditor.availableLanguages
70+
CodeEditor.availableThemes
71+
```
72+
73+
They can be used in a SwiftUI `Picker` like so:
74+
75+
```swift
76+
struct MyEditor: View {
77+
78+
@State private var source = "let it = be"
79+
@State private var language = CodeEditor.Language.swift
80+
81+
var body: some View {
82+
Picker("Language", selection: $language) {
83+
ForEach(CodeEditor.availableLanguages) { language in
84+
Text("\(language.rawValue.capitalized)")
85+
.tag(language)
86+
}
87+
}
88+
89+
CodeEditor(source: $source, language: language)
90+
}
91+
}
92+
```
93+
94+
Note: The `CodeEditor` doesn't do automatic theme changes if the appearance
95+
changes.
96+
97+
### Font Sizing
98+
99+
On macOS the editor supports sizing of the font (using Cmd +/Cmd - and the
100+
font panel).
101+
To enable sizing commands, the WindowScene needs to have the proper commands
102+
applied, e.g.:
103+
104+
```swift
105+
WindowGroup {
106+
ContentView()
107+
}
108+
.commands {
109+
TextFormattingCommands()
110+
}
111+
```
112+
To persist the size, the `fontSize` binding is available.
113+
114+
115+
### Who
116+
117+
SVGWebView is brought to you by [ZeeZide](https://zeezide.de).
118+
We like feedback, GitHub stars, cool contract work,
119+
presumably any form of praise you can think of.
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
//
2+
// CodeEditor.swift
3+
// CodeEditor
4+
//
5+
// Created by Helge Heß.
6+
// Copyright © 2021 ZeeZide GmbH. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
import Highlightr
11+
12+
/**
13+
* An simple code editor (or viewer) with highlighting for SwiftUI (iOS and
14+
* macOS).
15+
*
16+
* To use the code editor as a Viewer, simply pass the source code
17+
*
18+
* struct ContentView: View {
19+
*
20+
* var body: some View {
21+
* CodeEditor(source: "let a = 42")
22+
* }
23+
* }
24+
*
25+
* If it should act as an actual editor, pass it `Binding`:
26+
*
27+
* struct ContentView: View {
28+
*
29+
* @State private var source = "let a = 42\n"
30+
*
31+
* var body: some View {
32+
* CodeEditor(source: $source, language: .swift, theme: .ocean)
33+
* }
34+
* }
35+
*
36+
* ### Languages and Themes
37+
*
38+
* Highlight.js supports more than 180 languages and over 80 different themes.
39+
*
40+
* The available languages and themes can be accessed using:
41+
*
42+
* CodeEditor.availableLanguages
43+
* CodeEditor.availableThemes
44+
*
45+
* They can be used in a SwiftUI `Picker` like so:
46+
*
47+
* @State var source = "let it = be"
48+
* @State var language = CodeEditor.Language.swift
49+
*
50+
* Picker("Language", selection: $language) {
51+
* ForEach(CodeEditor.availableLanguages) { language in
52+
* Text("\(language.rawValue.capitalized)")
53+
* .tag(language)
54+
* }
55+
* }
56+
*
57+
* CodeEditor(source: $source, language: language)
58+
*
59+
* Note: The `CodeEditor` doesn't do automatic theme changes if the appearance
60+
* changes.
61+
*
62+
* ### Font Sizing
63+
*
64+
* On macOS the editor supports sizing of the font (using Cmd +/Cmd - and the
65+
* font panel).
66+
* To enable sizing commands, the WindowScene needs to have the proper commands
67+
* applied, e.g.:
68+
*
69+
* WindowGroup {
70+
* ContentView()
71+
* }
72+
* .commands {
73+
* TextFormattingCommands()
74+
* }
75+
*
76+
* To persist the binding, the `fontSize` binding is available.
77+
*
78+
* ### Highlightr and Shaper
79+
*
80+
* Based on the excellent [Highlightr](https://github.com/raspu/Highlightr).
81+
* This means that it is using JavaScriptCore as the actual driver. As
82+
* Highlightr says:
83+
*
84+
* > It will never be as fast as a native solution, but it's fast enough to be
85+
* > used on a real time editor.
86+
*
87+
* The editor is similar to (but not exactly the same) the one used by
88+
* [SVG Shaper for SwiftUI](https://zeezide.de/en/products/svgshaper/),
89+
* for its SVG and Swift editor parts.
90+
*/
91+
public struct CodeEditor: View {
92+
93+
/// Returns the available themes in the associated Highlightr package.
94+
public static var availableThemes =
95+
Highlightr()?.availableThemes().map(ThemeName.init).sorted() ?? []
96+
97+
/// Returns the available languages in the associated Highlightr package.
98+
public static var availableLanguages =
99+
Highlightr()?.supportedLanguages().map(Language.init).sorted() ?? []
100+
101+
102+
/**
103+
* Flags available for `CodeEditor`, currently just:
104+
* - `.editable`
105+
* - `.selectable`
106+
*/
107+
@frozen public struct Flags: OptionSet {
108+
public let rawValue : UInt8
109+
@inlinable public init(rawValue: UInt8) { self.rawValue = rawValue }
110+
111+
/// `.editable` requires that the `source` of the `CodeEditor` is a
112+
/// `Binding`.
113+
public static let editable = Flags(rawValue: 1 << 0)
114+
115+
/// Whether the displayed content should be selectable by the user.
116+
public static let selectable = Flags(rawValue: 1 << 1)
117+
}
118+
119+
/**
120+
* Configures a CodeEditor View with the given parameters.
121+
*
122+
* - Parameters:
123+
* - source: A binding to a String that holds the source code to be edited
124+
* (or displayed).
125+
* - language: Optionally set a language (e.g. `.swift`), otherwise
126+
* Highlight.js will attempt to detect the language.
127+
* - theme: The name of the theme to use, defaults to "pojoaque".
128+
* - fontSize: On macOS this Binding can be used to persist the size of
129+
* the font in use. At runtime this is combined with the
130+
* theme to produce the full font information. (optional)
131+
* - flags: Configure whether the text is editable and/or selectable
132+
* (defaults to both).
133+
*/
134+
public init(source : Binding<String>,
135+
language : Language? = nil,
136+
theme : ThemeName = .default,
137+
fontSize : Binding<CGFloat>? = nil,
138+
flags : Flags = [ .selectable, .editable ])
139+
{
140+
self.source = source
141+
self.fontSize = fontSize
142+
self.language = language
143+
self.themeName = theme
144+
self.flags = flags
145+
}
146+
147+
/**
148+
* Configures a read-only CodeEditor View with the given parameters.
149+
*
150+
* - Parameters:
151+
* - source: A String that holds the source code to be displayed.
152+
* - language: Optionally set a language (e.g. `.swift`), otherwise
153+
* Highlight.js will attempt to detect the language.
154+
* - theme: The name of the theme to use, defaults to "pojoaque".
155+
* - fontSize: On macOS this Binding can be used to persist the size of
156+
* the font in use. At runtime this is combined with the
157+
* theme to produce the full font information. (optional)
158+
* - flags: Configure whether the text is selectable
159+
* (defaults to both).
160+
*/
161+
@inlinable
162+
public init(source : String,
163+
language : Language? = nil,
164+
theme : ThemeName = .default,
165+
fontSize : Binding<CGFloat>? = nil,
166+
flags : Flags = [ .selectable ])
167+
{
168+
assert(!flags.contains(.editable), "Editing requires a Binding")
169+
self.init(source : .constant(source),
170+
language : language,
171+
theme : theme,
172+
fontSize : fontSize,
173+
flags : flags.subtracting(.editable))
174+
}
175+
176+
private var source : Binding<String>
177+
private var fontSize : Binding<CGFloat>?
178+
private let language : Language?
179+
private let themeName : ThemeName
180+
private let flags : Flags
181+
private let inset = CGSize(width: 8, height: 8)
182+
183+
public var body: some View {
184+
UXCodeTextViewRepresentable(source : source,
185+
language : language,
186+
theme : themeName,
187+
fontSize : fontSize,
188+
flags : flags)
189+
}
190+
}
191+
192+
struct CodeEditor_Previews: PreviewProvider {
193+
194+
static var previews: some View {
195+
196+
CodeEditor(source: "let a = 5")
197+
.frame(width: 200, height: 100)
198+
199+
CodeEditor(source: "let a = 5", language: .swift, theme: .pojoaque)
200+
.frame(width: 200, height: 100)
201+
202+
CodeEditor(source:
203+
#"""
204+
The quadratic formula is $-b \pm \sqrt{b^2 - 4ac} \over 2a$
205+
\bye
206+
"""#, language: .tex
207+
)
208+
.frame(width: 540, height: 200)
209+
}
210+
}

0 commit comments

Comments
 (0)