SwiftUI syntax highlighter autobracket undeletable

40 Views Asked by At

I have a syntax highlighting App that usually works fine but the auto brackets closing part doesn’t work. When I type ( it add ) but I can’t delete that, however when I move the cursor I somehow can delete them.

I tried everything I could but wasn’t able to fix it. Feel free to change the whole thing if you want to.

Lexer.swift:

let swiftlexer = [
    Keyword(word: "let", color: highlight_pink),
    Keyword(word: "var", color: highlight_pink),
    Keyword(word: "if", color: highlight_pink),
    Keyword(word: "else", color: highlight_pink),
    Keyword(word: "for", color: highlight_pink)
]


ContentView.swift:

import SwiftUI

struct Keyword {
    var word: String
    var color: UIColor
}

let highlight_purple = UIColor(red: 70/255, green: 30/255, blue: 160/255, alpha: 1.0)
let highlight_pink = UIColor(red: 160/255, green: 30/255, blue: 160/255, alpha: 1.0)
let highlight_number = UIColor(red: 0/255, green: 0/255, blue: 255/255, alpha: 1.0)
let highlight_string = UIColor(red: 190/255, green: 60/255, blue: 40/255, alpha: 1.0)

var keywords = swiftlexer
// variable is predefined and works fine


class TextColorUpdater: ObservableObject {
    @Published var attributedText: NSAttributedString
    init(initialText: String) {
        self.attributedText = TextColorUpdater.highlightText(initialText)
    }
    func addClosingParenthesis() {
        // let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText)
        // mutableAttributedString.append(NSAttributedString(string: ")"))
        attributedText = TextColorUpdater.highlightText(attributedText.string + ")")
        update()
    }
    func update() {
        self.attributedText = TextColorUpdater.highlightText(attributedText.string)
    }
    static func highlightText(_ text: String) -> NSAttributedString {
        let mutableAttributedString = NSMutableAttributedString(string: text)
        mutableAttributedString.addAttribute(.font, value: UIFont(name: "Menlo", size: 25) as Any, range: NSRange(location: 0, length: mutableAttributedString.length))
        
        // Regular expression for detecting numbers (including decimal numbers)
        if let regex = try? NSRegularExpression(pattern: "\\b(\\d+\\.?\\d*)\\b", options: .caseInsensitive) {
            let matches = regex.matches(in: text, options: [], range: NSRange(location: 0, length: text.utf16.count))
            for match in matches {
                let nsRange = match.range
                mutableAttributedString.addAttribute(.foregroundColor, value: highlight_number, range: nsRange)
            }
        }
         
        if let stringRegex = try? NSRegularExpression(pattern: "\"([^\"]*)\"", options: .caseInsensitive) {
            let stringMatches = stringRegex.matches(in: text, options: [], range: NSRange(location: 0, length: text.utf16.count))
            for match in stringMatches {
                let quoteRange = match.range(at: 0) 
                let textRange = match.range(at: 1)  
                mutableAttributedString.addAttribute(.foregroundColor, value: highlight_string, range: quoteRange)
                mutableAttributedString.addAttribute(.foregroundColor, value: highlight_string, range: textRange)
            }
        }
        for keyword in keywords {
            if let regex = try? NSRegularExpression(pattern: "\\b\(keyword.word)\\b", options: .caseInsensitive) {
                let matches = regex.matches(in: text, options: [], range: NSRange(location: 0, length: text.utf16.count))
                for match in matches {
                    let nsRange = match.range
                    mutableAttributedString.addAttribute(.foregroundColor, value: keyword.color, range: nsRange)
                }
            }
        }
        return mutableAttributedString
    }
}
struct HighlightedTextEditor: UIViewRepresentable {
    @Binding var attributedText: NSAttributedString
    @MainActor
    class Coordinator: NSObject, UITextViewDelegate {
        let textView = UITextView()
        var textDidChange: ((NSAttributedString) -> Void)?
        override init() {
            super.init()
            textView.isEditable = true
            textView.keyboardType = .asciiCapable
            textView.isSelectable = true
            textView.delegate = self
        }
        func textViewDidChange(_ textView: UITextView) {
            textDidChange?(textView.attributedText)
        }
    }
    func makeUIView(context: Context) -> UITextView {
        context.coordinator.textView
    }
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.attributedText = attributedText
        context.coordinator.textDidChange = { attributedText = $0 }
    }
}
struct ContentView: View {
    @StateObject var textColorUpdater = TextColorUpdater(initialText: "")
    
    @State private var oldText = ""
    
    var body: some View {
        VStack {
            HighlightedTextEditor(attributedText: $textColorUpdater.attributedText)
                .onChange(of: textColorUpdater.attributedText, {
                    let newString = textColorUpdater.attributedText.string
                    if newString.last == "(" {
                        textColorUpdater.addClosingParenthesis()
                    }
                    textColorUpdater.update()
                    oldText = newString
                })
        }
    }
}
@main
struct YourApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
} 
0

There are 0 best solutions below