forked from intitni/CopilotForXcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOpenAIPromptToCodeAPI.swift
More file actions
143 lines (120 loc) · 5.41 KB
/
OpenAIPromptToCodeAPI.swift
File metadata and controls
143 lines (120 loc) · 5.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import CopilotModel
import CopilotService
import Foundation
import OpenAIService
final class OpenAIPromptToCodeAPI: PromptToCodeAPI {
var service: (any ChatGPTServiceType)?
func stopResponding() {
Task {
await service?.stopReceivingMessage()
}
}
func modifyCode(
code: String,
language: CopilotLanguage,
indentSize: Int,
usesTabsForIndentation: Bool,
requirement: String,
projectRootURL: URL,
fileURL: URL,
allCode: String,
extraSystemPrompt: String?
) async throws -> AsyncThrowingStream<(code: String, description: String), Error> {
let userPreferredLanguage = UserDefaults.shared.value(for: \.chatGPTLanguage)
let textLanguage = userPreferredLanguage.isEmpty ? "" : "in \(userPreferredLanguage)"
let prompt = {
let indentRule = usesTabsForIndentation ? "\(indentSize) tabs" : "\(indentSize) spaces"
if code.isEmpty {
return """
You are a senior programer in writing code in \(language.rawValue).
File url: \(fileURL)
\(extraSystemPrompt ?? "")
Please write a piece of code that meets my requirements. The indentation should be \(
indentRule
).
Please reply to me start with the code block, followed by a clear and concise description in 1-3 sentences about what you did \(
textLanguage
).
"""
} else {
return """
# Description
You are a senior programer in writing code in \(language.rawValue).
File url: \(fileURL)
\(extraSystemPrompt ?? "")
Please mutate the following code fragment with my requirements. Keep the original indentation. Do not add comments unless told to.
Please reply to me start with the code block followed by a clear and concise description about what you did in 1-3 sentences \(
textLanguage
).
# Code
```\(language.rawValue)
\(code)
```
"""
}
}()
let chatGPTService = ChatGPTService(systemPrompt: prompt, temperature: 0.3)
service = chatGPTService
let stream = try await chatGPTService.send(content: requirement)
return .init { continuation in
Task {
var content = ""
var extracted = extractCodeAndDescription(from: content)
do {
for try await fragment in stream {
content.append(fragment)
extracted = extractCodeAndDescription(from: content)
if !content.isEmpty, extracted.code.isEmpty {
continuation.yield((code: content, description: ""))
} else {
continuation.yield(extracted)
}
}
continuation.finish()
} catch {
continuation.finish(throwing: error)
}
}
}
}
func extractCodeAndDescription(from content: String) -> (code: String, description: String) {
func extractCodeFromMarkdown(_ markdown: String) -> (code: String, endIndex: Int)? {
let codeBlockRegex = try! NSRegularExpression(
pattern: #"```(?:\w+)?[\n]([\s\S]+?)[\n]```"#,
options: .dotMatchesLineSeparators
)
let range = NSRange(markdown.startIndex..<markdown.endIndex, in: markdown)
if let match = codeBlockRegex.firstMatch(in: markdown, options: [], range: range) {
let codeBlockRange = Range(match.range(at: 1), in: markdown)!
return (String(markdown[codeBlockRange]), match.range(at: 0).upperBound)
}
let incompleteCodeBlockRegex = try! NSRegularExpression(
pattern: #"```(?:\w+)?[\n]([\s\S]+?)$"#,
options: .dotMatchesLineSeparators
)
let range2 = NSRange(markdown.startIndex..<markdown.endIndex, in: markdown)
if let match = incompleteCodeBlockRegex.firstMatch(
in: markdown,
options: [],
range: range2
) {
let codeBlockRange = Range(match.range(at: 1), in: markdown)!
return (String(markdown[codeBlockRange]), match.range(at: 0).upperBound)
}
return nil
}
guard let (code, endIndex) = extractCodeFromMarkdown(content) else {
return ("", "")
}
func extractDescriptionFromMarkdown(_ markdown: String, startIndex: Int) -> String {
let startIndex = markdown.index(markdown.startIndex, offsetBy: startIndex)
guard startIndex < markdown.endIndex else { return "" }
let range = startIndex..<markdown.endIndex
let description = String(markdown[range])
.trimmingCharacters(in: .whitespacesAndNewlines)
return description
}
let description = extractDescriptionFromMarkdown(content, startIndex: endIndex)
return (code, description)
}
}