Skip to content

Commit 7d8609f

Browse files
committed
Make /run run on a interactive shell
1 parent 2d6f44b commit 7d8609f

File tree

3 files changed

+141
-1
lines changed

3 files changed

+141
-1
lines changed

Core/Sources/ChatPlugins/TerminalChatPlugin.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public actor TerminalChatPlugin: ChatPlugin {
4848

4949
let output = terminal.streamCommand(
5050
shell,
51-
arguments: ["-l", "-c", content],
51+
arguments: ["-i", "-l", "-c", content],
5252
currentDirectoryPath: projectURL?.path ?? fileURL.path,
5353
environment: [
5454
"PROJECT_ROOT": projectURL?.path ?? fileURL.path,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import CopilotModel
2+
import CopilotService
3+
import Foundation
4+
import OpenAIService
5+
6+
final class CopilotPromptToCodeAPI: PromptToCodeAPI {
7+
func stopResponding() {
8+
fatalError()
9+
}
10+
11+
func modifyCode(
12+
code: String,
13+
language: CopilotLanguage,
14+
indentSize: Int,
15+
usesTabsForIndentation: Bool,
16+
requirement: String
17+
) async throws -> AsyncThrowingStream<(code: String, description: String), Error> {
18+
fatalError()
19+
}
20+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import CopilotModel
2+
import CopilotService
3+
import Foundation
4+
import OpenAIService
5+
6+
final class OpenAIPromptToCodeAPI: PromptToCodeAPI {
7+
var service: (any ChatGPTServiceType)?
8+
9+
func stopResponding() {
10+
Task {
11+
await service?.stopReceivingMessage()
12+
}
13+
}
14+
15+
func modifyCode(
16+
code: String,
17+
language: CopilotLanguage,
18+
indentSize: Int,
19+
usesTabsForIndentation: Bool,
20+
requirement: String
21+
) async throws -> AsyncThrowingStream<(code: String, description: String), Error> {
22+
let userPreferredLanguage = UserDefaults.shared.value(for: \.chatGPTLanguage)
23+
let textLanguage = userPreferredLanguage.isEmpty ? "" : "in \(userPreferredLanguage)"
24+
25+
let prompt = {
26+
let indentRule = usesTabsForIndentation ? "\(indentSize) tabs" : "\(indentSize) spaces"
27+
if code.isEmpty {
28+
return """
29+
You are a senior programer in writing code in \(language.rawValue).
30+
31+
Please write a piece of code that meets my requirements. The indentation should be \(
32+
indentRule
33+
).
34+
35+
Please reply to me start with the code block, followed by a clear and concise description in 1-3 sentences about what you did \(
36+
textLanguage
37+
).
38+
"""
39+
} else {
40+
return """
41+
You are a senior programer in writing code in \(language.rawValue).
42+
43+
Please mutate the following code fragment with my requirements. Keep the original indentation. Do not add comments unless told to.
44+
45+
Please reply to me start with the code block followed by a clear and concise description about what you did in 1-3 sentences \(
46+
textLanguage
47+
).
48+
49+
```
50+
\(code)
51+
```
52+
"""
53+
}
54+
}()
55+
56+
let chatGPTService = ChatGPTService(systemPrompt: prompt, temperature: 0.5)
57+
service = chatGPTService
58+
let stream = try await chatGPTService.send(content: requirement)
59+
return .init { continuation in
60+
Task {
61+
var content = ""
62+
do {
63+
for try await fragment in stream {
64+
content.append(fragment)
65+
continuation.yield(extractCodeAndDescription(from: content))
66+
}
67+
continuation.finish()
68+
} catch {
69+
continuation.finish(throwing: error)
70+
}
71+
}
72+
}
73+
}
74+
75+
func extractCodeAndDescription(from content: String) -> (code: String, description: String) {
76+
func extractCodeFromMarkdown(_ markdown: String) -> (code: String, endIndex: Int)? {
77+
let codeBlockRegex = try! NSRegularExpression(
78+
pattern: #"```(?:\w+)?[\n]([\s\S]+?)[\n]```"#,
79+
options: .dotMatchesLineSeparators
80+
)
81+
let range = NSRange(markdown.startIndex..<markdown.endIndex, in: markdown)
82+
if let match = codeBlockRegex.firstMatch(in: markdown, options: [], range: range) {
83+
let codeBlockRange = Range(match.range(at: 1), in: markdown)!
84+
return (String(markdown[codeBlockRange]), match.range(at: 0).upperBound)
85+
}
86+
87+
let incompleteCodeBlockRegex = try! NSRegularExpression(
88+
pattern: #"```(?:\w+)?[\n]([\s\S]+?)$"#,
89+
options: .dotMatchesLineSeparators
90+
)
91+
let range2 = NSRange(markdown.startIndex..<markdown.endIndex, in: markdown)
92+
if let match = incompleteCodeBlockRegex.firstMatch(
93+
in: markdown,
94+
options: [],
95+
range: range2
96+
) {
97+
let codeBlockRange = Range(match.range(at: 1), in: markdown)!
98+
return (String(markdown[codeBlockRange]), match.range(at: 0).upperBound)
99+
}
100+
return nil
101+
}
102+
103+
guard let (code, endIndex) = extractCodeFromMarkdown(content) else {
104+
return ("", "")
105+
}
106+
107+
func extractDescriptionFromMarkdown(_ markdown: String, startIndex: Int) -> String {
108+
let startIndex = markdown.index(markdown.startIndex, offsetBy: startIndex)
109+
guard startIndex < markdown.endIndex else { return "" }
110+
let range = startIndex..<markdown.endIndex
111+
let description = String(markdown[range])
112+
.trimmingCharacters(in: .whitespacesAndNewlines)
113+
return description
114+
}
115+
116+
let description = extractDescriptionFromMarkdown(content, startIndex: endIndex)
117+
118+
return (code, description)
119+
}
120+
}

0 commit comments

Comments
 (0)