Skip to content

Commit 5ca3bf0

Browse files
committed
Merge branch 'feature/keyword-base-retrieval' into develop
2 parents 9f528c6 + e9dfeb1 commit 5ca3bf0

8 files changed

Lines changed: 217 additions & 40 deletions

File tree

Core/Sources/HostApp/DebugView.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ final class DebugSettings: ObservableObject {
1616
var disableGitHubCopilotSettingsAutoRefreshOnAppear
1717
@AppStorage(\.useUserDefaultsBaseAPIKeychain) var useUserDefaultsBaseAPIKeychain
1818
@AppStorage(\.disableEnhancedWorkspace) var disableEnhancedWorkspace
19+
@AppStorage(\.disableGitIgnoreCheck) var disableGitIgnoreCheck
1920
init() {}
2021
}
2122

@@ -64,6 +65,10 @@ struct DebugSettingsView: View {
6465
Text("Disable Enhanced Workspace")
6566
}
6667

68+
Toggle(isOn: $settings.disableGitIgnoreCheck) {
69+
Text("Disable Git Ignore Check")
70+
}
71+
6772
Button("Reset Migration Version to 0") {
6873
UserDefaults.shared.set(nil, forKey: "OldMigrationVersion")
6974
}

Pro

Submodule Pro updated from 9eee25c to 33771b2

Tool/Package.swift

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ let package = Package(
4242
"AppActivator",
4343
]
4444
),
45+
.library(name: "GitIgnoreCheck", targets: ["GitIgnoreCheck"]),
4546
],
4647
dependencies: [
4748
// A fork of https://github.com/aespinilla/Tiktoken to allow loading from local files.
@@ -202,6 +203,7 @@ let package = Package(
202203
.target(
203204
name: "Workspace",
204205
dependencies: [
206+
"GitIgnoreCheck",
205207
"UserDefaultsObserver",
206208
"SuggestionModel",
207209
"Environment",
@@ -235,6 +237,18 @@ let package = Package(
235237
dependencies: ["FocusedCodeFinder"]
236238
),
237239

240+
.target(
241+
name: "GitIgnoreCheck",
242+
dependencies: [
243+
"Terminal",
244+
"Preferences",
245+
.product(
246+
name: "ComposableArchitecture",
247+
package: "swift-composable-architecture"
248+
),
249+
]
250+
),
251+
238252
// MARK: - Services
239253

240254
.target(
@@ -302,7 +316,7 @@ let package = Package(
302316
.product(
303317
name: "ComposableArchitecture",
304318
package: "swift-composable-architecture"
305-
)
319+
),
306320
]
307321
),
308322
.testTarget(
@@ -312,7 +326,7 @@ let package = Package(
312326
.product(
313327
name: "ComposableArchitecture",
314328
package: "swift-composable-architecture"
315-
)
329+
),
316330
]
317331
),
318332

@@ -344,6 +358,7 @@ let package = Package(
344358
"Preferences",
345359
"FocusedCodeFinder",
346360
"XcodeInspector",
361+
"GitIgnoreCheck",
347362
],
348363
path: "Sources/ChatContextCollectors/ActiveDocumentChatContextCollector"
349364
),

Tool/Sources/ChatContextCollectors/ActiveDocumentChatContextCollector/ActiveDocumentChatContextCollector.swift

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import ASTParser
22
import ChatContextCollector
3+
import Dependencies
34
import FocusedCodeFinder
45
import Foundation
6+
import GitIgnoreCheck
57
import OpenAIService
68
import Preferences
79
import SuggestionModel
@@ -12,22 +14,27 @@ public final class ActiveDocumentChatContextCollector: ChatContextCollector {
1214

1315
public var activeDocumentContext: ActiveDocumentContext?
1416

17+
@Dependency(\.gitIgnoredChecker) var gitIgnoredChecker
18+
1519
public func generateContext(
1620
history: [ChatMessage],
1721
scopes: Set<ChatContext.Scope>,
1822
content: String,
1923
configuration: ChatGPTConfiguration
20-
) -> ChatContext {
24+
) async -> ChatContext {
2125
guard let info = getEditorInformation() else { return .empty }
2226
let context = getActiveDocumentContext(info)
2327
activeDocumentContext = context
2428

25-
guard scopes.contains(.code) else {
29+
let isSensitive = await gitIgnoredChecker.checkIfGitIgnored(fileURL: info.documentURL)
30+
31+
guard scopes.contains(.code)
32+
else {
2633
if scopes.contains(.file) {
2734
var removedCode = context
2835
removedCode.focusedContext = nil
2936
return .init(
30-
systemPrompt: extractSystemPrompt(removedCode),
37+
systemPrompt: extractSystemPrompt(removedCode, isSensitive: isSensitive),
3138
retrievedContent: [],
3239
functions: []
3340
)
@@ -37,36 +44,39 @@ public final class ActiveDocumentChatContextCollector: ChatContextCollector {
3744

3845
var functions = [any ChatGPTFunction]()
3946

40-
// When the bot is already focusing on a piece of code, it can expand the range.
47+
if !isSensitive {
48+
// When the bot is already focusing on a piece of code, it can expand the range.
4149

42-
if context.focusedContext != nil {
43-
functions.append(ExpandFocusRangeFunction(contextCollector: self))
44-
}
50+
if context.focusedContext != nil {
51+
functions.append(ExpandFocusRangeFunction(contextCollector: self))
52+
}
4553

46-
// When the bot is not focusing on any code, or the focusing area is not the user's
47-
// selection, it can move the focus back to the user's selection.
54+
// When the bot is not focusing on any code, or the focusing area is not the user's
55+
// selection, it can move the focus back to the user's selection.
4856

49-
if context.focusedContext == nil ||
50-
!(context.focusedContext?.codeRange.contains(context.selectionRange) ?? false)
51-
{
52-
functions.append(MoveToFocusedCodeFunction(contextCollector: self))
53-
}
57+
if context.focusedContext == nil ||
58+
!(context.focusedContext?.codeRange.contains(context.selectionRange) ?? false)
59+
{
60+
functions.append(MoveToFocusedCodeFunction(contextCollector: self))
61+
}
5462

55-
// When there is a line annotation not in the focused area, the bot can move the focus area
56-
// to the code covering the line of the annotation.
63+
// When there is a line annotation not in the focused area, the bot can move the focus
64+
// area
65+
// to the code covering the line of the annotation.
5766

58-
if let focusedContext = context.focusedContext,
59-
!focusedContext.otherLineAnnotations.isEmpty
60-
{
61-
functions.append(MoveToCodeAroundLineFunction(contextCollector: self))
62-
}
67+
if let focusedContext = context.focusedContext,
68+
!focusedContext.otherLineAnnotations.isEmpty
69+
{
70+
functions.append(MoveToCodeAroundLineFunction(contextCollector: self))
71+
}
6372

64-
if context.focusedContext == nil, !context.lineAnnotations.isEmpty {
65-
functions.append(MoveToCodeAroundLineFunction(contextCollector: self))
73+
if context.focusedContext == nil, !context.lineAnnotations.isEmpty {
74+
functions.append(MoveToCodeAroundLineFunction(contextCollector: self))
75+
}
6676
}
6777

6878
return .init(
69-
systemPrompt: extractSystemPrompt(context),
79+
systemPrompt: extractSystemPrompt(context, isSensitive: isSensitive),
7080
retrievedContent: [],
7181
functions: functions
7282
)
@@ -90,7 +100,7 @@ public final class ActiveDocumentChatContextCollector: ChatContextCollector {
90100
return activeDocumentContext
91101
}
92102

93-
func extractSystemPrompt(_ context: ActiveDocumentContext) -> String {
103+
func extractSystemPrompt(_ context: ActiveDocumentContext, isSensitive: Bool) -> String {
94104
let start = """
95105
## File and Code Scope
96106
@@ -108,7 +118,7 @@ public final class ActiveDocumentChatContextCollector: ChatContextCollector {
108118
let language = "Language: \(context.language.rawValue)"
109119

110120
if let focusedContext = context.focusedContext {
111-
let codeContext = focusedContext.context.isEmpty
121+
let codeContext = focusedContext.context.isEmpty || isSensitive
112122
? ""
113123
: """
114124
Focused Context:
@@ -119,14 +129,16 @@ public final class ActiveDocumentChatContextCollector: ChatContextCollector {
119129

120130
let codeRange = "Focused Range [line, character]: \(focusedContext.codeRange)"
121131

122-
let code = """
123-
Focused Code (start from line \(focusedContext.codeRange.start.line + 1)):
124-
```\(context.language.rawValue)
125-
\(focusedContext.code)
126-
```
127-
"""
132+
let code = context.selectionRange.isEmpty && isSensitive
133+
? ""
134+
: """
135+
Focused Code (start from line \(focusedContext.codeRange.start.line + 1)):
136+
```\(context.language.rawValue)
137+
\(focusedContext.code)
138+
```
139+
"""
128140

129-
let fileAnnotations = focusedContext.otherLineAnnotations.isEmpty
141+
let fileAnnotations = focusedContext.otherLineAnnotations.isEmpty || isSensitive
130142
? ""
131143
: """
132144
Other Annotations:\"""
@@ -139,7 +151,7 @@ public final class ActiveDocumentChatContextCollector: ChatContextCollector {
139151
\"""
140152
"""
141153

142-
let codeAnnotations = focusedContext.lineAnnotations.isEmpty
154+
let codeAnnotations = focusedContext.lineAnnotations.isEmpty || isSensitive
143155
? ""
144156
: """
145157
Annotations Inside Focused Range:\"""
@@ -165,7 +177,7 @@ public final class ActiveDocumentChatContextCollector: ChatContextCollector {
165177
.joined(separator: "\n\n")
166178
} else {
167179
let selectionRange = "Selection Range [line, character]: \(context.selectionRange)"
168-
let lineAnnotations = context.lineAnnotations.isEmpty
180+
let lineAnnotations = context.lineAnnotations.isEmpty || isSensitive
169181
? ""
170182
: """
171183
Line Annotations:\"""
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import Dependencies
2+
import Foundation
3+
import Terminal
4+
import Preferences
5+
6+
public struct CheckIfGitIgnoredDependencyKey: DependencyKey {
7+
public static var liveValue: GitIgnoredChecker = DefaultGitIgnoredChecker()
8+
public static var testValue: GitIgnoredChecker = DefaultGitIgnoredChecker(isTest: true)
9+
}
10+
11+
public extension DependencyValues {
12+
var gitIgnoredChecker: GitIgnoredChecker {
13+
get { self[CheckIfGitIgnoredDependencyKey.self] }
14+
set { self[CheckIfGitIgnoredDependencyKey.self] = newValue }
15+
}
16+
}
17+
18+
public protocol GitIgnoredChecker {
19+
func checkIfGitIgnored(fileURL: URL) async -> Bool
20+
func checkIfGitIgnored(fileURLs: [URL]) async -> [URL]
21+
}
22+
23+
public extension GitIgnoredChecker {
24+
func checkIfGitIgnored(filePath: String) async -> Bool {
25+
await checkIfGitIgnored(fileURL: URL(fileURLWithPath: filePath))
26+
}
27+
28+
func checkIfGitIgnored(filePaths: [String]) async -> [String] {
29+
await checkIfGitIgnored(fileURLs: filePaths.map { URL(fileURLWithPath: $0) })
30+
.map(\.path)
31+
}
32+
}
33+
34+
public struct DefaultGitIgnoredChecker: GitIgnoredChecker {
35+
var isTest = false
36+
37+
var noCheck: Bool {
38+
if isTest { return false }
39+
return UserDefaults.shared.value(for: \.disableGitIgnoreCheck)
40+
}
41+
42+
public init() {}
43+
44+
init(isTest: Bool) {
45+
self.isTest = isTest
46+
}
47+
48+
public func checkIfGitIgnored(fileURL: URL) async -> Bool {
49+
if noCheck { return false }
50+
let terminal = Terminal()
51+
guard let gitFolderURL = gitFolderURL(forFileURL: fileURL) else {
52+
return false
53+
}
54+
do {
55+
_ = try await terminal.runCommand(
56+
"/bin/bash",
57+
arguments: ["-c", "check-ignore \"\(fileURL.path)\""],
58+
currentDirectoryPath: gitFolderURL.path,
59+
environment: [:]
60+
)
61+
return true
62+
} catch {
63+
return false
64+
}
65+
}
66+
67+
public func checkIfGitIgnored(fileURLs: [URL]) async -> [URL] {
68+
if noCheck { return [] }
69+
let filePaths = fileURLs.map { "\"\($0.path)\"" }.joined(separator: " ")
70+
guard let firstFileURL = fileURLs.first else { return [] }
71+
let terminal = Terminal()
72+
guard let gitFolderURL = gitFolderURL(forFileURL: firstFileURL) else {
73+
return []
74+
}
75+
do {
76+
let result = try await terminal.runCommand(
77+
"/bin/bash",
78+
arguments: ["-c", "check-ignore \(filePaths)"],
79+
currentDirectoryPath: gitFolderURL.path,
80+
environment: [:]
81+
)
82+
return result
83+
.split(separator: "\n")
84+
.map(String.init)
85+
.compactMap(URL.init(fileURLWithPath:))
86+
} catch {
87+
return []
88+
}
89+
}
90+
}
91+
92+
func gitFolderURL(forFileURL fileURL: URL) -> URL? {
93+
var currentURL = fileURL
94+
let fileManager = FileManager.default
95+
while currentURL.path != "/" {
96+
let gitFolderURL = currentURL.appendingPathComponent(".git")
97+
if fileManager.fileExists(atPath: gitFolderURL.path) {
98+
return currentURL
99+
}
100+
currentURL = currentURL.deletingLastPathComponent()
101+
}
102+
return nil
103+
}
104+

Tool/Sources/Preferences/Keys.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,10 @@ public extension UserDefaultPreferenceKeys {
546546
key: "FeatureFlag-DisableGitHubCopilotSettingsAutoRefreshOnAppear"
547547
)
548548
}
549+
550+
var disableGitIgnoreCheck: FeatureFlag {
551+
.init(defaultValue: false, key: "FeatureFlag-DisableGitIgnoreCheck")
552+
}
549553

550554
var disableEnhancedWorkspace: FeatureFlag {
551555
.init(

0 commit comments

Comments
 (0)