Improve JSON editor for macOS
This commit is contained in:
@@ -16,6 +16,10 @@ struct MainView: View {
|
||||
@State private var showConnections = false
|
||||
@State private var buttonState = ButtonVisibilityState()
|
||||
|
||||
private let profileEditor: (Binding<String>, Bool) -> AnyView = { text, isEditable in
|
||||
AnyView(RunestoneTextView(text: text, isEditable: isEditable))
|
||||
}
|
||||
|
||||
private var shouldShowBottomAccessory: Bool {
|
||||
guard !environments.extensionProfileLoading else {
|
||||
return false
|
||||
@@ -124,6 +128,7 @@ struct MainView: View {
|
||||
.environment(\.selection, $selection)
|
||||
.environment(\.importProfile, $importProfile)
|
||||
.environment(\.importRemoteProfile, $importRemoteProfile)
|
||||
.environment(\.profileEditor, profileEditor)
|
||||
.handlesExternalEvents(preferring: [], allowing: ["*"])
|
||||
.onOpenURL(perform: openURL)
|
||||
.sheet(isPresented: $showGroups) {
|
||||
@@ -160,6 +165,7 @@ struct MainView: View {
|
||||
.environment(\.selection, $selection)
|
||||
.environment(\.importProfile, $importProfile)
|
||||
.environment(\.importRemoteProfile, $importRemoteProfile)
|
||||
.environment(\.profileEditor, profileEditor)
|
||||
.handlesExternalEvents(preferring: [], allowing: ["*"])
|
||||
.onOpenURL(perform: openURL)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import Runestone
|
||||
import UIKit
|
||||
|
||||
final class ProfileEditorTheme: Theme {
|
||||
let font: UIFont = .monospacedSystemFont(ofSize: 14, weight: .regular)
|
||||
let textColor: UIColor = .label
|
||||
|
||||
let gutterBackgroundColor: UIColor = .secondarySystemBackground
|
||||
let gutterHairlineColor: UIColor = .separator
|
||||
|
||||
let lineNumberColor: UIColor = .secondaryLabel
|
||||
let lineNumberFont: UIFont = .monospacedSystemFont(ofSize: 14, weight: .regular)
|
||||
|
||||
let selectedLineBackgroundColor: UIColor = .systemFill
|
||||
let selectedLinesLineNumberColor: UIColor = .label
|
||||
let selectedLinesGutterBackgroundColor: UIColor = .secondarySystemBackground
|
||||
|
||||
let invisibleCharactersColor: UIColor = .tertiaryLabel
|
||||
|
||||
let pageGuideHairlineColor: UIColor = .separator
|
||||
let pageGuideBackgroundColor: UIColor = .secondarySystemBackground
|
||||
|
||||
let markedTextBackgroundColor: UIColor = .systemFill
|
||||
let markedTextBackgroundCornerRadius: CGFloat = 4
|
||||
|
||||
func textColor(for rawHighlightName: String) -> UIColor? {
|
||||
guard let highlightName = HighlightName(rawHighlightName) else {
|
||||
return nil
|
||||
}
|
||||
switch highlightName {
|
||||
case .comment:
|
||||
return .secondaryLabel
|
||||
case .property:
|
||||
return .systemCyan
|
||||
case .string:
|
||||
return .systemGreen
|
||||
case .number:
|
||||
return .systemOrange
|
||||
case .constantBuiltin:
|
||||
return .systemPurple
|
||||
case .error:
|
||||
return .systemRed
|
||||
}
|
||||
}
|
||||
|
||||
func fontTraits(for _: String) -> FontTraits {
|
||||
[]
|
||||
}
|
||||
}
|
||||
|
||||
private enum HighlightName: String {
|
||||
case comment
|
||||
case property
|
||||
case string
|
||||
case number
|
||||
case constantBuiltin = "constant.builtin"
|
||||
case error
|
||||
|
||||
init?(_ rawHighlightName: String) {
|
||||
var components = rawHighlightName.split(separator: ".")
|
||||
while !components.isEmpty {
|
||||
let candidateRawHighlightName = components.joined(separator: ".")
|
||||
if let highlightName = Self(rawValue: candidateRawHighlightName) {
|
||||
self = highlightName
|
||||
return
|
||||
}
|
||||
components.removeLast()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import Runestone
|
||||
import SwiftUI
|
||||
import TreeSitterJSON5Runestone
|
||||
|
||||
struct RunestoneTextView: UIViewRepresentable {
|
||||
@Binding var text: String
|
||||
let isEditable: Bool
|
||||
|
||||
func makeUIView(context: Context) -> TextView {
|
||||
let textView = TextView()
|
||||
|
||||
textView.showLineNumbers = true
|
||||
textView.isLineWrappingEnabled = false
|
||||
textView.showTabs = false
|
||||
textView.showSpaces = false
|
||||
textView.showLineBreaks = false
|
||||
textView.showSoftLineBreaks = false
|
||||
textView.showNonBreakingSpaces = false
|
||||
|
||||
textView.autocorrectionType = .no
|
||||
textView.autocapitalizationType = .none
|
||||
textView.smartDashesType = .no
|
||||
textView.smartQuotesType = .no
|
||||
textView.smartInsertDeleteType = .no
|
||||
|
||||
textView.backgroundColor = .secondarySystemGroupedBackground
|
||||
textView.contentInsetAdjustmentBehavior = .always
|
||||
textView.alwaysBounceVertical = true
|
||||
|
||||
textView.kern = 0.3
|
||||
textView.lineHeightMultiplier = 1.3
|
||||
|
||||
textView.characterPairs = [
|
||||
BasicCharacterPair(leading: "{", trailing: "}"),
|
||||
BasicCharacterPair(leading: "[", trailing: "]"),
|
||||
BasicCharacterPair(leading: "\"", trailing: "\""),
|
||||
]
|
||||
|
||||
let theme = ProfileEditorTheme()
|
||||
let state = TextViewState(text: text, theme: theme, language: .json5)
|
||||
textView.setState(state)
|
||||
|
||||
textView.isEditable = isEditable
|
||||
textView.editorDelegate = context.coordinator
|
||||
|
||||
return textView
|
||||
}
|
||||
|
||||
func updateUIView(_ textView: TextView, context _: Context) {
|
||||
if textView.text != text {
|
||||
textView.text = text
|
||||
}
|
||||
if textView.isEditable != isEditable {
|
||||
textView.isEditable = isEditable
|
||||
}
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(self)
|
||||
}
|
||||
|
||||
final class Coordinator: TextViewDelegate {
|
||||
var parent: RunestoneTextView
|
||||
|
||||
init(_ parent: RunestoneTextView) {
|
||||
self.parent = parent
|
||||
}
|
||||
|
||||
func textViewDidChange(_ textView: TextView) {
|
||||
parent.text = textView.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class BasicCharacterPair: CharacterPair {
|
||||
let leading: String
|
||||
let trailing: String
|
||||
|
||||
init(leading: String, trailing: String) {
|
||||
self.leading = leading
|
||||
self.trailing = trailing
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user