Skip to content

Commit 2d6f44b

Browse files
committed
Merge branch 'feature/continous-prompt-to-code' into develop
2 parents 2633668 + d8da2bc commit 2d6f44b

File tree

12 files changed

+183
-97
lines changed

12 files changed

+183
-97
lines changed

Core/Sources/CopilotModel/ExportedFromLSP.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ public extension CursorPosition {
99

1010
public extension CursorRange {
1111
static var outOfScope: CursorRange { .init(start: .outOfScope, end: .outOfScope) }
12+
static func cursor(_ position: CursorPosition) -> CursorRange {
13+
return .init(start: position, end: position)
14+
}
1215
}

Core/Sources/PromptToCodeService/PromptToCodeService.swift

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public final class PromptToCodeService: ObservableObject {
1515
@Published public var code: String
1616
@Published public var isResponding: Bool = false
1717
@Published public var description: String = ""
18+
@Published public var isContinuous = false
1819
public var oldDescription: String?
1920
public var canRevert: Bool { oldCode != nil }
2021
public var selectionRange: CursorRange
@@ -40,15 +41,15 @@ public final class PromptToCodeService: ObservableObject {
4041
let api = promptToCodeAPI
4142
runningAPI = api
4243
isResponding = true
43-
let toBemodified = code
44+
let toBeModified = code
4445
oldDescription = description
4546
oldCode = code
4647
code = ""
4748
description = ""
4849
defer { isResponding = false }
4950
do {
5051
let stream = try await api.modifyCode(
51-
code: toBemodified,
52+
code: toBeModified,
5253
language: language,
5354
indentSize: indentSize,
5455
usesTabsForIndentation: usesTabsForIndentation,
@@ -60,7 +61,13 @@ public final class PromptToCodeService: ObservableObject {
6061
description = fragment.description
6162
}
6263
}
64+
} catch is CancellationError {
65+
return
6366
} catch {
67+
if (error as NSError).code == NSURLErrorCancelled {
68+
return
69+
}
70+
6471
if let oldCode {
6572
code = oldCode
6673
}
@@ -80,7 +87,7 @@ public final class PromptToCodeService: ObservableObject {
8087
description = oldDescription
8188
}
8289
self.oldCode = nil
83-
self.oldDescription = nil
90+
oldDescription = nil
8491
}
8592

8693
public func generateCompletion() -> CopilotCompletion {
@@ -127,6 +134,9 @@ final class OpenAIPromptToCodeAPI: PromptToCodeAPI {
127134
usesTabsForIndentation: Bool,
128135
requirement: String
129136
) async throws -> AsyncThrowingStream<(code: String, description: String), Error> {
137+
let userPreferredLanguage = UserDefaults.shared.value(for: \.chatGPTLanguage)
138+
let textLanguage = userPreferredLanguage.isEmpty ? "" : "in \(userPreferredLanguage)"
139+
130140
let prompt = {
131141
let indentRule = usesTabsForIndentation ? "\(indentSize) tabs" : "\(indentSize) spaces"
132142
if code.isEmpty {
@@ -137,7 +147,9 @@ final class OpenAIPromptToCodeAPI: PromptToCodeAPI {
137147
indentRule
138148
).
139149
140-
Please reply to me start with the code block, followed by a short description in 1-3 sentences about what you did.
150+
Please reply to me start with the code block, followed by a short description in 1-3 sentences about what you did \(
151+
textLanguage
152+
).
141153
"""
142154
} else {
143155
return """
@@ -147,7 +159,9 @@ final class OpenAIPromptToCodeAPI: PromptToCodeAPI {
147159
indentRule
148160
).
149161
150-
Please reply to me start with the code block followed by a short description about what you did in 1-3 sentences.
162+
Please reply to me start with the code block followed by a short description about what you did in 1-3 sentences \(
163+
textLanguage
164+
).
151165
152166
```
153167
\(code)

Core/Sources/Service/GUI/PromptToCodeProvider+Service.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ extension PromptToCodeProvider {
1717
service.$code.sink(receiveValue: set(\.code)).store(in: &cancellables)
1818
service.$isResponding.sink(receiveValue: set(\.isResponding)).store(in: &cancellables)
1919
service.$description.sink(receiveValue: set(\.description)).store(in: &cancellables)
20+
service.$isContinuous.sink(receiveValue: set(\.isContinuous)).store(in: &cancellables)
2021
service.$oldCode.map { $0 != nil }
2122
.sink(receiveValue: set(\.canRevert)).store(in: &cancellables)
2223

@@ -34,6 +35,8 @@ extension PromptToCodeProvider {
3435
Task { [weak self] in
3536
do {
3637
try await service.modifyCode(prompt: requirement)
38+
} catch is CancellationError {
39+
return
3740
} catch {
3841
Task { @MainActor [weak self] in
3942
self?.errorMessage = error.localizedDescription
@@ -52,6 +55,10 @@ extension PromptToCodeProvider {
5255
await handler.acceptSuggestion()
5356
}
5457
}
58+
59+
onContinuousToggleClick = {
60+
service.isContinuous.toggle()
61+
}
5562
}
5663

5764
func set<T>(_ keyPath: WritableKeyPath<PromptToCodeProvider, T>) -> (T) -> Void {

Core/Sources/Service/SuggestionCommandHandler/CommentBaseCommandHandler.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ struct CommentBaseCommandHandler: SuggestionCommandHandler {
103103

104104
return .init(
105105
content: String(lines.joined(separator: "")),
106-
newCursor: cursorPosition,
106+
newSelection: .cursor(cursorPosition),
107107
modifications: extraInfo.modifications
108108
)
109109
}

Core/Sources/Service/SuggestionCommandHandler/PseudoCommandHandler.swift

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ struct PseudoCommandHandler {
7070

7171
func acceptSuggestion() async {
7272
if UserDefaults.shared.value(for: \.acceptSuggestionWithAccessibilityAPI) {
73-
guard let xcode = ActiveApplicationMonitor.activeXcode else { return }
73+
guard let xcode = ActiveApplicationMonitor.activeXcode ?? ActiveApplicationMonitor.latestXcode else { return }
7474
let application = AXUIElementCreateApplication(xcode.processIdentifier)
7575
guard let focusElement = application.focusedElement,
7676
focusElement.description == "Source Editor"
@@ -108,8 +108,8 @@ struct PseudoCommandHandler {
108108
.presentErrorMessage("Fail to set editor content.")
109109
}
110110

111-
if let cursor = result.newCursor {
112-
var range = convertCursorPositionToRange(cursor, in: result.content)
111+
if let selection = result.newSelection {
112+
var range = convertCursorRangeToRange(selection, in: result.content)
113113
if let value = AXValueCreate(.cfRange, &range) {
114114
AXUIElementSetAttributeValue(
115115
focusElement,
@@ -218,21 +218,28 @@ private extension PseudoCommandHandler {
218218
)
219219
}
220220

221-
// a function to convert CursorPosition(line:character:) into a Range(location:length) in given
222-
// content
223-
func convertCursorPositionToRange(
224-
_ cursorPosition: CursorPosition,
221+
func convertCursorRangeToRange(
222+
_ cursorRange: CursorRange,
225223
in content: String
226224
) -> CFRange {
227225
let lines = content.breakLines()
228-
var count = 0
226+
var countS = 0
227+
var countE = 0
228+
var range = CFRange(location: 0, length: 0)
229229
for (i, line) in lines.enumerated() {
230-
if i == cursorPosition.line {
231-
return CFRange(location: count + cursorPosition.character, length: 0)
230+
if i == cursorRange.start.line {
231+
countS = countS + cursorRange.start.character
232+
range.location = countS
233+
}
234+
if i == cursorRange.end.line {
235+
countE = countE + cursorRange.end.character
236+
range.length = max(countE - range.location, 0)
237+
break
232238
}
233-
count += line.count
239+
countS += line.count
240+
countE += line.count
234241
}
235-
return CFRange(location: count, length: 0)
242+
return range
236243
}
237244
}
238245

Core/Sources/Service/SuggestionCommandHandler/WindowBaseCommandHandler.swift

Lines changed: 73 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -136,61 +136,68 @@ struct WindowBaseCommandHandler: SuggestionCommandHandler {
136136
func acceptSuggestion(editor: EditorContent) async throws -> UpdatedContent? {
137137
presenter.markAsProcessing(true)
138138
defer { presenter.markAsProcessing(false) }
139-
139+
140140
let fileURL = try await Environment.fetchCurrentFileURL()
141141
let (workspace, _) = try await Workspace.fetchOrCreateWorkspaceIfNeeded(fileURL: fileURL)
142142

143-
let result: (suggestion: CopilotCompletion, cleanup: () -> Void)? = {
144-
if let service = WidgetDataSource.shared.promptToCodes[fileURL]?.promptToCodeService {
145-
return (CopilotCompletion(
146-
text: service.code,
147-
position: service.selectionRange.start,
148-
uuid: UUID().uuidString,
149-
range: service.selectionRange,
150-
displayText: service.code
151-
), {
152-
WidgetDataSource.shared.removePromptToCode(for: fileURL)
153-
presenter.closePromptToCode(fileURL: fileURL)
154-
})
155-
}
156-
157-
if let acceptedSuggestion = workspace.acceptSuggestion(
158-
forFileAt: fileURL,
159-
editor: editor
160-
) {
161-
return (acceptedSuggestion, {
162-
presenter.discardSuggestion(fileURL: fileURL)
163-
})
164-
}
165-
166-
return nil
167-
}()
168-
169-
guard let result else { return nil }
170-
171143
let injector = SuggestionInjector()
172144
var lines = editor.lines
173145
var cursorPosition = editor.cursorPosition
174146
var extraInfo = SuggestionInjector.ExtraInfo()
175-
injector.rejectCurrentSuggestions(
176-
from: &lines,
177-
cursorPosition: &cursorPosition,
178-
extraInfo: &extraInfo
179-
)
180-
injector.acceptSuggestion(
181-
intoContentWithoutSuggestion: &lines,
182-
cursorPosition: &cursorPosition,
183-
completion: result.suggestion,
184-
extraInfo: &extraInfo
185-
)
186147

187-
result.cleanup()
188-
189-
return .init(
190-
content: String(lines.joined(separator: "")),
191-
newCursor: cursorPosition,
192-
modifications: extraInfo.modifications
193-
)
148+
if let service = WidgetDataSource.shared.promptToCodes[fileURL]?.promptToCodeService {
149+
let suggestion = CopilotCompletion(
150+
text: service.code,
151+
position: service.selectionRange.start,
152+
uuid: UUID().uuidString,
153+
range: service.selectionRange,
154+
displayText: service.code
155+
)
156+
157+
injector.acceptSuggestion(
158+
intoContentWithoutSuggestion: &lines,
159+
cursorPosition: &cursorPosition,
160+
completion: suggestion,
161+
extraInfo: &extraInfo
162+
)
163+
164+
if service.isContinuous {
165+
service.selectionRange = .init(
166+
start: service.selectionRange.start,
167+
end: cursorPosition
168+
)
169+
presenter.presentPromptToCode(fileURL: fileURL)
170+
} else {
171+
WidgetDataSource.shared.removePromptToCode(for: fileURL)
172+
presenter.closePromptToCode(fileURL: fileURL)
173+
}
174+
175+
return .init(
176+
content: String(lines.joined(separator: "")),
177+
newSelection: .init(start: service.selectionRange.start, end: cursorPosition),
178+
modifications: extraInfo.modifications
179+
)
180+
} else if let acceptedSuggestion = workspace.acceptSuggestion(
181+
forFileAt: fileURL,
182+
editor: editor
183+
) {
184+
injector.acceptSuggestion(
185+
intoContentWithoutSuggestion: &lines,
186+
cursorPosition: &cursorPosition,
187+
completion: acceptedSuggestion,
188+
extraInfo: &extraInfo
189+
)
190+
191+
presenter.discardSuggestion(fileURL: fileURL)
192+
193+
return .init(
194+
content: String(lines.joined(separator: "")),
195+
newSelection: .cursor(cursorPosition),
196+
modifications: extraInfo.modifications
197+
)
198+
}
199+
200+
return nil
194201
}
195202

196203
func presentRealtimeSuggestions(editor: EditorContent) async throws -> UpdatedContent? {
@@ -334,24 +341,30 @@ struct WindowBaseCommandHandler: SuggestionCommandHandler {
334341
presenter.markAsProcessing(true)
335342
defer { presenter.markAsProcessing(false) }
336343
let fileURL = try await Environment.fetchCurrentFileURL()
337-
let language = UserDefaults.shared.value(for: \.chatGPTLanguage)
338344
let codeLanguage = languageIdentifierFromFileURL(fileURL)
339-
let code = {
340-
guard let selection = editor.selections.last,
341-
selection.start != selection.end else { return "" }
342-
return editor.selectedCode(in: selection)
343-
}()
345+
346+
let (code, selection) = {
347+
guard var selection = editor.selections.last,
348+
selection.start != selection.end
349+
else { return ("", .cursor(editor.cursorPosition)) }
350+
if selection.start.line != selection.end.line {
351+
// when there are multiple lines start from char 0 so that it can keep the
352+
// indentation.
353+
selection.start = .init(line: selection.start.line, character: 0)
354+
}
355+
return (
356+
editor.selectedCode(in: selection),
357+
.init(
358+
start: .init(line: selection.start.line, character: selection.start.character),
359+
end: .init(line: selection.end.line, character: selection.end.character)
360+
)
361+
)
362+
}() as (String, CursorRange)
344363

345364
_ = await WidgetDataSource.shared.createPromptToCode(
346365
for: fileURL,
347366
code: code,
348-
selectionRange: editor.selections.last.map { .init(
349-
start: $0.start,
350-
end: $0.end
351-
) } ?? .init(
352-
start: editor.cursorPosition,
353-
end: editor.cursorPosition
354-
),
367+
selectionRange: selection,
355368
language: codeLanguage
356369
)
357370

Core/Sources/Service/SuggestionPresenter/PresentInCommentSuggestionPresenter.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct PresentInCommentSuggestionPresenter {
2525
guard let completion = await filespace.presentingSuggestion else {
2626
return .init(
2727
content: originalContent,
28-
newCursor: cursorPosition,
28+
newSelection: .cursor(cursorPosition),
2929
modifications: extraInfo.modifications
3030
)
3131
}
@@ -40,7 +40,7 @@ struct PresentInCommentSuggestionPresenter {
4040

4141
return .init(
4242
content: String(lines.joined(separator: "")),
43-
newCursor: cursorPosition,
43+
newSelection: .cursor(cursorPosition),
4444
modifications: extraInfo.modifications
4545
)
4646
}
@@ -65,7 +65,7 @@ struct PresentInCommentSuggestionPresenter {
6565

6666
return .init(
6767
content: String(lines.joined(separator: "")),
68-
newCursor: cursorPosition,
68+
newSelection: .cursor(cursorPosition),
6969
modifications: extraInfo.modifications
7070
)
7171
}

0 commit comments

Comments
 (0)