Skip to content

Commit c6e9a07

Browse files
committed
Pre-release 0.40.133
1 parent 0517f3b commit c6e9a07

File tree

75 files changed

+4099
-321
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+4099
-321
lines changed

Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/ExtensionService.xcscheme

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@
5050
reference = "container:Pro/ProTestPlan.xctestplan">
5151
</TestPlanReference>
5252
</TestPlans>
53+
<Testables>
54+
<TestableReference
55+
skipped = "NO">
56+
<BuildableReference
57+
BuildableIdentifier = "primary"
58+
BlueprintIdentifier = "GitHelperTests"
59+
BuildableName = "GitHelperTests"
60+
BlueprintName = "GitHelperTests"
61+
ReferencedContainer = "container:Tool">
62+
</BuildableReference>
63+
</TestableReference>
64+
</Testables>
5365
</TestAction>
5466
<LaunchAction
5567
buildConfiguration = "Debug"

Core/Package.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,9 @@ let package = Package(
182182
.product(name: "Terminal", package: "Tool"),
183183
.product(name: "SystemUtils", package: "Tool"),
184184
.product(name: "AppKitExtension", package: "Tool"),
185-
.product(name: "WebContentExtractor", package: "Tool")
185+
.product(name: "WebContentExtractor", package: "Tool"),
186+
.product(name: "GitHelper", package: "Tool"),
187+
.product(name: "SuggestionBasic", package: "Tool")
186188
]),
187189
.testTarget(
188190
name: "ChatServiceTests",

Core/Sources/ChatService/ChatInjector.swift

Lines changed: 97 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import XcodeInspector
44
import AXHelper
55
import ApplicationServices
66
import AppActivator
7-
7+
import LanguageServerProtocol
88

99
public 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

Comments
 (0)