diff --git a/src/EditorView.swift b/src/EditorView.swift index 7f46982..9c9e37f 100644 --- a/src/EditorView.swift +++ b/src/EditorView.swift @@ -1121,37 +1121,45 @@ struct EditorTextView: NSViewRepresentable { func textView(_ textView: NSTextView, shouldChangeTextIn range: NSRange, replacementString text: String?) -> Bool { guard let text = text, text.count == 1 else { return true } let ch = text.first! + let hasSelection = range.length > 0 - let closers: [Character: Character] = ["}": "{", ")": "(", "]": "["] - if let opener = closers[ch] { - let str = textView.string as NSString - if range.location < str.length { - let next = str.character(at: range.location) - if next == ch.asciiValue.map({ UInt16($0) }) ?? 0 { - textView.setSelectedRange(NSRange(location: range.location + 1, length: 0)) - return false + // Skip over matching closer when cursor is right before it + if !hasSelection { + let closerChars: Set = ["}", ")", "]", "\"", "'"] + if closerChars.contains(ch) { + let str = textView.string as NSString + if range.location < str.length { + let next = Character(UnicodeScalar(str.character(at: range.location))!) + if next == ch { + textView.setSelectedRange(NSRange(location: range.location + 1, length: 0)) + return false + } } } } - let pairs: [Character: String] = ["{": "}", "(": ")", "[": "]"] - if let close = pairs[ch] { - textView.insertText(String(ch) + close, replacementRange: range) - textView.setSelectedRange(NSRange(location: range.location + 1, length: 0)) + let pairClosers: [Character: Character] = ["{": "}", "(": ")", "[": "]"] + if let close = pairClosers[ch] { + if hasSelection { + let selected = (textView.string as NSString).substring(with: range) + textView.insertText(String(ch) + selected + String(close), replacementRange: range) + textView.setSelectedRange(NSRange(location: range.location + 1, length: selected.count)) + } else { + textView.insertText(String(ch) + String(close), replacementRange: range) + textView.setSelectedRange(NSRange(location: range.location + 1, length: 0)) + } return false } if ch == "\"" || ch == "'" { - let str = textView.string as NSString - if range.location < str.length { - let next = str.character(at: range.location) - if next == ch.asciiValue.map({ UInt16($0) }) ?? 0 { - textView.setSelectedRange(NSRange(location: range.location + 1, length: 0)) - return false - } + if hasSelection { + let selected = (textView.string as NSString).substring(with: range) + textView.insertText(String(ch) + selected + String(ch), replacementRange: range) + textView.setSelectedRange(NSRange(location: range.location + 1, length: selected.count)) + } else { + textView.insertText(String(ch) + String(ch), replacementRange: range) + textView.setSelectedRange(NSRange(location: range.location + 1, length: 0)) } - textView.insertText(String(ch) + String(ch), replacementRange: range) - textView.setSelectedRange(NSRange(location: range.location + 1, length: 0)) return false } @@ -1207,17 +1215,16 @@ struct EditorTextView: NSViewRepresentable { continue } - let closesFirst = trimmed.hasPrefix("}") || trimmed.hasPrefix(")") || trimmed.hasPrefix("]") - if closesFirst && depth > 0 { depth -= 1 } + let opens = trimmed.filter { "{([".contains($0) }.count + let closes = trimmed.filter { "})]".contains($0) }.count + let delta = opens - closes + + if delta < 0 { depth = max(0, depth + delta) } let indent = String(repeating: " ", count: depth) result.append(indent + trimmed) - let opens = trimmed.filter { "{([".contains($0) }.count - let closes = trimmed.filter { "})]".contains($0) }.count - depth += opens - closes - if closesFirst { depth += 1; depth -= 1 } - if depth < 0 { depth = 0 } + if delta > 0 { depth += delta } } return result.joined(separator: "\n") @@ -1244,14 +1251,13 @@ struct EditorTextView: NSViewRepresentable { indent += " " } - let cursorBeforeLineEnd = cursor < NSMaxRange(lineRange) - 1 let charBeforeCursor: Character? = cursor > 0 ? Character(UnicodeScalar(str.character(at: cursor - 1))!) : nil let charAtCursor: Character? = cursor < str.length ? Character(UnicodeScalar(str.character(at: cursor))!) : nil // Between matching pairs: insert extra newline if let before = charBeforeCursor, let after = charAtCursor, (before == "{" && after == "}") || (before == "(" && after == ")") || (before == "[" && after == "]") { - let baseIndent = String(indent.dropLast(4).isEmpty ? "" : indent.dropLast(4)) + let baseIndent = indent.count >= 4 ? String(indent.dropLast(4)) : "" let insertion = "\n" + indent + "\n" + baseIndent textView.insertText(insertion, replacementRange: textView.selectedRange()) textView.setSelectedRange(NSRange(location: cursor + 1 + indent.count, length: 0))