Skip to content

Commit ffd9d43

Browse files
committed
Update prompt to code panel
1 parent 128425b commit ffd9d43

File tree

14 files changed

+836
-488
lines changed

14 files changed

+836
-488
lines changed

Core/Package.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ let package = Package(
8686
.product(name: "UserDefaultsObserver", package: "Tool"),
8787
.product(name: "AppMonitoring", package: "Tool"),
8888
.product(name: "SuggestionBasic", package: "Tool"),
89+
.product(name: "PromptToCode", package: "Tool"),
8990
.product(name: "ChatTab", package: "Tool"),
9091
.product(name: "Logger", package: "Tool"),
9192
.product(name: "OpenAIService", package: "Tool"),
@@ -152,6 +153,7 @@ let package = Package(
152153
.target(
153154
name: "PromptToCodeService",
154155
dependencies: [
156+
.product(name: "PromptToCode", package: "Tool"),
155157
.product(name: "FocusedCodeFinder", package: "Tool"),
156158
.product(name: "SuggestionBasic", package: "Tool"),
157159
.product(name: "OpenAIService", package: "Tool"),
@@ -219,6 +221,7 @@ let package = Package(
219221
dependencies: [
220222
"PromptToCodeService",
221223
"ChatGPTChatTab",
224+
.product(name: "PromptToCode", package: "Tool"),
222225
.product(name: "Toast", package: "Tool"),
223226
.product(name: "UserDefaultsObserver", package: "Tool"),
224227
.product(name: "SharedUIComponents", package: "Tool"),

Core/Sources/Service/GUI/GraphicalUserInterfaceController.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ import ChatTabPersistent
2121
@Reducer
2222
struct GUI {
2323
@ObservableState
24-
struct State: Equatable {
24+
struct State {
2525
var suggestionWidgetState = Widget.State()
2626

27-
var chatTabGroup: ChatPanel.ChatTabGroup {
27+
var chatTabGroup: SuggestionWidget.ChatPanel.ChatTabGroup {
2828
get { suggestionWidgetState.chatPanelState.chatTabGroup }
2929
set { suggestionWidgetState.chatPanelState.chatTabGroup = newValue }
3030
}
@@ -85,7 +85,7 @@ struct GUI {
8585
var body: some ReducerOf<Self> {
8686
CombineReducers {
8787
Scope(state: \.suggestionWidgetState, action: \.suggestionWidget) {
88-
WidgetFeature()
88+
Widget()
8989
}
9090

9191
Scope(

Core/Sources/Service/SuggestionCommandHandler/WindowBaseCommandHandler.swift

Lines changed: 68 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import AppKit
22
import ChatService
3+
import ComposableArchitecture
34
import Foundation
45
import GitHubCopilotService
56
import LanguageServerProtocol
67
import Logger
78
import OpenAIService
8-
import SuggestionInjector
99
import SuggestionBasic
10+
import SuggestionInjector
1011
import SuggestionWidget
1112
import UserNotifications
1213
import Workspace
@@ -175,61 +176,78 @@ struct WindowBaseCommandHandler: SuggestionCommandHandler {
175176

176177
let injector = SuggestionInjector()
177178
var lines = editor.lines
179+
var ranges = [CursorRange]()
178180
var cursorPosition = editor.cursorPosition
179181
var extraInfo = SuggestionInjector.ExtraInfo()
180182

181183
let store = await Service.shared.guiController.store
182184

183185
if let promptToCode = store.state.promptToCodeGroup.activePromptToCode {
184-
if promptToCode.isAttachedToSelectionRange, promptToCode.documentURL != fileURL {
186+
if promptToCode.promptToCodeState.isAttachedToTarget,
187+
promptToCode.promptToCodeState.source.documentURL != fileURL
188+
{
185189
return nil
186190
}
187191

188-
let range = {
189-
if promptToCode.isAttachedToSelectionRange,
190-
let range = promptToCode.selectionRange
191-
{
192-
return range
193-
}
194-
return editor.selections.first.map {
195-
CursorRange(start: $0.start, end: $0.end)
196-
} ?? CursorRange(
197-
start: editor.cursorPosition,
198-
end: editor.cursorPosition
192+
for snippet in promptToCode.promptToCodeState.snippets.sorted(by: { a, b in
193+
a.attachedRange.start.line > b.attachedRange.start.line
194+
}) {
195+
let range = {
196+
if promptToCode.promptToCodeState.isAttachedToTarget {
197+
return snippet.attachedRange
198+
}
199+
return editor.selections.first.map {
200+
CursorRange(start: $0.start, end: $0.end)
201+
} ?? CursorRange(
202+
start: editor.cursorPosition,
203+
end: editor.cursorPosition
204+
)
205+
}()
206+
207+
ranges.append(range)
208+
209+
let suggestion = CodeSuggestion(
210+
id: UUID().uuidString,
211+
text: snippet.modifiedCode,
212+
position: range.start,
213+
range: range
199214
)
200-
}()
201215

202-
let suggestion = CodeSuggestion(
203-
id: UUID().uuidString,
204-
text: promptToCode.code,
205-
position: range.start,
206-
range: range
207-
)
216+
injector.acceptSuggestion(
217+
intoContentWithoutSuggestion: &lines,
218+
cursorPosition: &cursorPosition,
219+
completion: suggestion,
220+
extraInfo: &extraInfo
221+
)
208222

209-
injector.acceptSuggestion(
210-
intoContentWithoutSuggestion: &lines,
211-
cursorPosition: &cursorPosition,
212-
completion: suggestion,
213-
extraInfo: &extraInfo
214-
)
223+
_ = await Task { @MainActor [cursorPosition] in
224+
await store.send(
225+
.promptToCodeGroup(.updatePromptToCodeRange(
226+
id: promptToCode.id,
227+
snippetId: snippet.id,
228+
range: .init(start: range.start, end: cursorPosition)
229+
))
230+
).finish()
231+
}.value
232+
}
215233

216-
_ = await Task { @MainActor [cursorPosition] in
217-
store.send(
218-
.promptToCodeGroup(.updatePromptToCodeRange(
219-
id: promptToCode.id,
220-
range: .init(start: range.start, end: cursorPosition)
221-
))
222-
)
234+
_ = await MainActor.run {
223235
store.send(
224236
.promptToCodeGroup(.discardAcceptedPromptToCodeIfNotContinuous(
225237
id: promptToCode.id
226238
))
227239
)
228-
}.result
240+
}
229241

230242
return .init(
231243
content: String(lines.joined(separator: "")),
232-
newSelection: .init(start: range.start, end: cursorPosition),
244+
newSelection: {
245+
if ranges.isEmpty {
246+
.init(start: cursorPosition, end: cursorPosition)
247+
} else {
248+
ranges.last
249+
}
250+
}(),
233251
modifications: extraInfo.modifications
234252
)
235253
}
@@ -405,22 +423,24 @@ extension WindowBaseCommandHandler {
405423
}
406424

407425
_ = await Task { @MainActor in
408-
// if there is already a prompt to code presenting, we should not present another one
409426
store.send(.promptToCodeGroup(.activateOrCreatePromptToCode(.init(
410-
code: code,
411-
selectionRange: selection,
412-
language: codeLanguage,
413-
identSize: filespace.codeMetadata.indentSize ?? 4,
427+
promptToCodeState: Shared(.init(
428+
source: .init(
429+
language: codeLanguage,
430+
documentURL: fileURL,
431+
projectRootURL: workspace.projectRootURL,
432+
content: editor.content,
433+
lines: editor.lines
434+
),
435+
originalCode: code,
436+
attachedRange: selection,
437+
instruction: newPrompt ?? "",
438+
extraSystemPrompt: newExtraSystemPrompt ?? ""
439+
)),
440+
indentSize: filespace.codeMetadata.indentSize ?? 4,
414441
usesTabsForIndentation: filespace.codeMetadata.usesTabsForIndentation ?? false,
415-
documentURL: fileURL,
416-
projectRootURL: workspace.projectRootURL,
417-
allCode: editor.content,
418-
allLines: editor.lines,
419-
isContinuous: isContinuous,
420442
commandName: name,
421-
defaultPrompt: newPrompt ?? "",
422-
extraSystemPrompt: newExtraSystemPrompt,
423-
generateDescriptionRequirement: generateDescription
443+
isContinuous: isContinuous
424444
))))
425445
}.result
426446
}

Core/Sources/SuggestionWidget/FeatureReducers/PromptToCodeGroup.swift

Lines changed: 20 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import XcodeInspector
77
@Reducer
88
public struct PromptToCodeGroup {
99
@ObservableState
10-
public struct State: Equatable {
10+
public struct State {
1111
public var promptToCodes: IdentifiedArrayOf<PromptToCodePanel.State> = []
1212
public var activeDocumentURL: PromptToCodePanel.State.ID? = XcodeInspector.shared
1313
.realtimeActiveDocumentURL
1414
public var activePromptToCode: PromptToCodePanel.State? {
1515
get {
16-
if let detached = promptToCodes.first(where: { !$0.isAttachedToSelectionRange }) {
16+
if let detached = promptToCodes
17+
.first(where: { !$0.promptToCodeState.isAttachedToTarget })
18+
{
1719
return detached
1820
}
1921
guard let id = activeDocumentURL else { return nil }
@@ -27,60 +29,15 @@ public struct PromptToCodeGroup {
2729
}
2830
}
2931

30-
public struct PromptToCodeInitialState: Equatable {
31-
public var code: String
32-
public var selectionRange: CursorRange?
33-
public var language: CodeLanguage
34-
public var identSize: Int
35-
public var usesTabsForIndentation: Bool
36-
public var documentURL: URL
37-
public var projectRootURL: URL
38-
public var allCode: String
39-
public var allLines: [String]
40-
public var isContinuous: Bool
41-
public var commandName: String?
42-
public var defaultPrompt: String
43-
public var extraSystemPrompt: String?
44-
public var generateDescriptionRequirement: Bool?
45-
46-
public init(
47-
code: String,
48-
selectionRange: CursorRange?,
49-
language: CodeLanguage,
50-
identSize: Int,
51-
usesTabsForIndentation: Bool,
52-
documentURL: URL,
53-
projectRootURL: URL,
54-
allCode: String,
55-
allLines: [String],
56-
isContinuous: Bool,
57-
commandName: String?,
58-
defaultPrompt: String,
59-
extraSystemPrompt: String?,
60-
generateDescriptionRequirement: Bool?
61-
) {
62-
self.code = code
63-
self.selectionRange = selectionRange
64-
self.language = language
65-
self.identSize = identSize
66-
self.usesTabsForIndentation = usesTabsForIndentation
67-
self.documentURL = documentURL
68-
self.projectRootURL = projectRootURL
69-
self.allCode = allCode
70-
self.allLines = allLines
71-
self.isContinuous = isContinuous
72-
self.commandName = commandName
73-
self.defaultPrompt = defaultPrompt
74-
self.extraSystemPrompt = extraSystemPrompt
75-
self.generateDescriptionRequirement = generateDescriptionRequirement
76-
}
77-
}
78-
79-
public enum Action: Equatable {
32+
public enum Action {
8033
/// Activate the prompt to code if it exists or create it if it doesn't
81-
case activateOrCreatePromptToCode(PromptToCodeInitialState)
82-
case createPromptToCode(PromptToCodeInitialState)
83-
case updatePromptToCodeRange(id: PromptToCodePanel.State.ID, range: CursorRange)
34+
case activateOrCreatePromptToCode(PromptToCodePanel.State)
35+
case createPromptToCode(PromptToCodePanel.State)
36+
case updatePromptToCodeRange(
37+
id: PromptToCodePanel.State.ID,
38+
snippetId: UUID,
39+
range: CursorRange
40+
)
8441
case discardAcceptedPromptToCodeIfNotContinuous(id: PromptToCodePanel.State.ID)
8542
case updateActivePromptToCode(documentURL: URL)
8643
case discardExpiredPromptToCode(documentURLs: [URL])
@@ -103,37 +60,22 @@ public struct PromptToCodeGroup {
10360
return .run { send in
10461
await send(.createPromptToCode(s))
10562
}
106-
case let .createPromptToCode(s):
107-
let newPromptToCode = PromptToCodePanel.State(
108-
code: s.code,
109-
prompt: s.defaultPrompt,
110-
language: s.language,
111-
indentSize: s.identSize,
112-
usesTabsForIndentation: s.usesTabsForIndentation,
113-
projectRootURL: s.projectRootURL,
114-
documentURL: s.documentURL,
115-
allCode: s.allCode,
116-
allLines: s.allLines,
117-
commandName: s.commandName,
118-
isContinuous: s.isContinuous,
119-
selectionRange: s.selectionRange,
120-
extraSystemPrompt: s.extraSystemPrompt,
121-
generateDescriptionRequirement: s.generateDescriptionRequirement
122-
)
63+
case let .createPromptToCode(newPromptToCode):
12364
// insert at 0 so it has high priority then the other detached prompt to codes
12465
state.promptToCodes.insert(newPromptToCode, at: 0)
12566
return .run { send in
126-
if !newPromptToCode.prompt.isEmpty {
67+
if !newPromptToCode.promptToCodeState.instruction.isEmpty {
12768
await send(.promptToCode(newPromptToCode.id, .modifyCodeButtonTapped))
12869
}
12970
}.cancellable(
13071
id: PromptToCodePanel.CancellationKey.modifyCode(newPromptToCode.id),
13172
cancelInFlight: true
13273
)
13374

134-
case let .updatePromptToCodeRange(id, range):
135-
if let p = state.promptToCodes[id: id], p.isAttachedToSelectionRange {
136-
state.promptToCodes[id: id]?.selectionRange = range
75+
case let .updatePromptToCodeRange(id, snippetId, range):
76+
if let p = state.promptToCodes[id: id], p.promptToCodeState.isAttachedToTarget {
77+
state.promptToCodes[id: id]?.promptToCodeState.snippets[id: snippetId]?
78+
.attachedRange = range
13779
}
13880
return .none
13981

@@ -153,7 +95,7 @@ public struct PromptToCodeGroup {
15395

15496
case .promptToCode:
15597
return .none
156-
98+
15799
case .activePromptToCode:
158100
return .none
159101
}
@@ -166,7 +108,7 @@ public struct PromptToCodeGroup {
166108
PromptToCodePanel()
167109
.dependency(\.promptToCodeService, promptToCodeServiceFactory())
168110
})
169-
111+
170112
Reduce { state, action in
171113
switch action {
172114
case let .promptToCode(id, .cancelButtonTapped):

0 commit comments

Comments
 (0)