@@ -4,7 +4,7 @@ import XcodeInspector
44import AXHelper
55import ApplicationServices
66import AppActivator
7-
7+ import LanguageServerProtocol
88
99public struct ChatInjector {
1010 public init ( ) { }
@@ -22,76 +22,126 @@ public struct ChatInjector {
2222 var lines = editorContent. content. splitByNewLine (
2323 omittingEmptySubsequences: false
2424 ) . map { String ( $0) }
25- // Ensure the line number is within the bounds of the file
25+
2626 guard cursorPosition. line <= lines. count else { return }
2727
2828 var modifications : [ Modification ] = [ ]
2929
30- // remove selection
31- // make sure there is selection exist and valid
30+ // Handle selection deletion
3231 if let selection = editorContent. selections. first,
33- selection. isValid,
34- selection. start. line < lines. endIndex {
32+ selection. isValid,
33+ selection. start. line < lines. endIndex {
3534 let selectionEndLine = min ( selection. end. line, lines. count - 1 )
3635 let deletedSelection = CursorRange (
3736 start: selection. start,
3837 end: . init( line: selectionEndLine, character: selection. end. character)
3938 )
4039 modifications. append ( . deletedSelection( deletedSelection) )
4140 lines = lines. applying ( [ . deletedSelection( deletedSelection) ] )
42-
43- // update cursorPosition to the start of selection
4441 cursorPosition = selection. start
4542 }
4643
47- let targetLine = lines [ cursorPosition. line]
44+ let insertionRange = CursorRange (
45+ start: cursorPosition,
46+ end: cursorPosition
47+ )
4848
49- // Determine the indention level of the target line
50- let leadingWhitespace = cursorPosition. character > 0 ? targetLine. prefix { $0. isWhitespace } : " "
51- let indentation = String ( leadingWhitespace)
49+ try Self . performInsertion (
50+ content: codeBlock,
51+ range: insertionRange,
52+ lines: & lines,
53+ modifications: & modifications,
54+ focusElement: focusElement
55+ )
5256
53- // Insert codeblock at the specified position
54- let index = targetLine. index ( targetLine. startIndex, offsetBy: min ( cursorPosition. character, targetLine. count) )
55- let before = targetLine [ ..< index]
56- let after = targetLine [ index... ]
57+ } catch {
58+ print ( " Failed to insert code block: \( error) " )
59+ }
60+ }
61+
62+ public static func insertSuggestion( suggestion: String , range: CursorRange , lines: [ String ] ) {
63+ do {
64+ guard let focusElement = XcodeInspector . shared. focusedElement,
65+ focusElement. description == " Source Editor "
66+ else { return }
5767
58- let codeBlockLines = codeBlock. splitByNewLine (
59- omittingEmptySubsequences: false
60- ) . enumerated ( ) . map { ( index, element) -> String in
61- return index == 0 ? String ( element) : indentation + String( element)
62- }
63-
64- var toBeInsertedLines = [ String] ( )
65- toBeInsertedLines. append ( String ( before) + codeBlockLines. first!)
66- toBeInsertedLines. append ( contentsOf: codeBlockLines. dropFirst ( ) . dropLast ( ) )
67- toBeInsertedLines. append ( codeBlockLines. last! + String( after) )
68+ guard range. start. line >= 0 ,
69+ range. start. line < lines. count,
70+ range. end. line >= 0 ,
71+ range. end. line < lines. count
72+ else { return }
6873
69- lines. replaceSubrange ( ( cursorPosition. line) ... ( cursorPosition. line) , with: toBeInsertedLines)
74+ var lines = lines
75+ var modifications : [ Modification ] = [ ]
7076
71- // Join the lines
72- let newContent = String ( lines. joined ( separator: " \n " ) )
77+ if range. isValid {
78+ modifications. append ( . deletedSelection( range) )
79+ lines = lines. applying ( [ . deletedSelection( range) ] )
80+ }
7381
74- // Inject updated content
75- let newCursorPosition = CursorPosition (
76- line: cursorPosition. line + codeBlockLines. count - 1 ,
77- character: codeBlockLines. last? . count ?? 0
78- )
79- modifications. append ( . inserted( cursorPosition. line, toBeInsertedLines) )
80- try AXHelper ( ) . injectUpdatedCodeWithAccessibilityAPI (
81- . init(
82- content: newContent,
83- newSelection: . cursor( newCursorPosition) ,
84- modifications: modifications
85- ) ,
86- focusElement: focusElement,
87- onSuccess: {
88- NSWorkspace . activatePreviousActiveXcode ( )
89- }
90-
82+ try performInsertion (
83+ content: suggestion,
84+ range: range,
85+ lines: & lines,
86+ modifications: & modifications,
87+ focusElement: focusElement
9188 )
9289
9390 } catch {
94- print ( " Failed to insert code block: \( error) " )
91+ print ( " Failed to insert suggestion: \( error) " )
92+ }
93+ }
94+
95+ private static func performInsertion(
96+ content: String ,
97+ range: CursorRange ,
98+ lines: inout [ String ] ,
99+ modifications: inout [ Modification ] ,
100+ focusElement: AXUIElement
101+ ) throws {
102+ let targetLine = lines [ range. start. line]
103+ let leadingWhitespace = range. start. character > 0 ? targetLine. prefix { $0. isWhitespace } : " "
104+ let indentation = String ( leadingWhitespace)
105+
106+ let index = targetLine. index ( targetLine. startIndex, offsetBy: min ( range. start. character, targetLine. count) )
107+ let before = targetLine [ ..< index]
108+ let after = targetLine [ index... ]
109+
110+ let contentLines = content. splitByNewLine (
111+ omittingEmptySubsequences: false
112+ ) . enumerated ( ) . map { ( index, element) -> String in
113+ return index == 0 ? String ( element) : indentation + String( element)
114+ }
115+
116+ var toBeInsertedLines = [ String] ( )
117+ if contentLines. count > 1 {
118+ toBeInsertedLines. append ( String ( before) + contentLines. first!)
119+ toBeInsertedLines. append ( contentsOf: contentLines. dropFirst ( ) . dropLast ( ) )
120+ toBeInsertedLines. append ( contentLines. last! + String( after) )
121+ } else {
122+ toBeInsertedLines. append ( String ( before) + contentLines. first! + String( after) )
95123 }
124+
125+ lines. replaceSubrange ( ( range. start. line) ... ( range. start. line) , with: toBeInsertedLines)
126+
127+ let newContent = String ( lines. joined ( separator: " \n " ) )
128+ let newCursorPosition = CursorPosition (
129+ line: range. start. line + contentLines. count - 1 ,
130+ character: contentLines. last? . count ?? 0
131+ )
132+
133+ modifications. append ( . inserted( range. start. line, toBeInsertedLines) )
134+
135+ try AXHelper ( ) . injectUpdatedCodeWithAccessibilityAPI (
136+ . init(
137+ content: newContent,
138+ newSelection: . cursor( newCursorPosition) ,
139+ modifications: modifications
140+ ) ,
141+ focusElement: focusElement,
142+ onSuccess: {
143+ NSWorkspace . activatePreviousActiveXcode ( )
144+ }
145+ )
96146 }
97147}
0 commit comments