Skip to content

Commit eab7301

Browse files
committed
Ignore pattern reorder fixes
1 parent 23148b3 commit eab7301

File tree

3 files changed

+103
-45
lines changed

3 files changed

+103
-45
lines changed

CodeEdit/Features/Settings/Pages/SourceControlSettings/Models/IgnorePatternModel.swift

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -87,58 +87,97 @@ class IgnorePatternModel: ObservableObject {
8787
}
8888

8989
func savePatterns() {
90-
// Suspend the file monitor to avoid self-triggered updates
91-
stopFileMonitor()
92-
93-
defer {
94-
startFileMonitor()
95-
}
90+
stopFileMonitor() // Suspend the file monitor to avoid self-triggered updates
91+
defer { startFileMonitor() }
9692

97-
// Get the file contents; if the file doesn't exist, create it with the patterns
9893
guard let fileContent = try? String(contentsOf: fileURL) else {
9994
writeAllPatterns()
10095
return
10196
}
10297

10398
let lines = fileContent.split(separator: "\n", omittingEmptySubsequences: false).map(String.init)
104-
var patternToLineIndex: [String: Int] = [:] // Map patterns to their line indices
105-
var reorderedLines: [String] = [] // Store the final reordered lines
99+
let (patternToLineIndex, nonPatternLines) = mapLines(lines)
100+
let globalCommentLines = extractGlobalComments(nonPatternLines, patternToLineIndex)
101+
102+
var reorderedLines = reorderPatterns(globalCommentLines, patternToLineIndex, nonPatternLines, lines)
103+
104+
// Ensure single blank line at the end
105+
reorderedLines = cleanUpWhitespace(in: reorderedLines)
106+
107+
// Write the updated content back to the file
108+
let updatedContent = reorderedLines.joined(separator: "\n")
109+
try? updatedContent.write(to: fileURL, atomically: true, encoding: .utf8)
110+
}
111+
112+
private func mapLines(_ lines: [String]) -> ([String: Int], [(line: String, index: Int)]) {
113+
var patternToLineIndex: [String: Int] = [:]
114+
var nonPatternLines: [(line: String, index: Int)] = []
106115

107-
// Map existing patterns in the file
108116
for (index, line) in lines.enumerated() {
109117
let trimmedLine = line.trimmingCharacters(in: .whitespaces)
110118
if !trimmedLine.isEmpty && !trimmedLine.hasPrefix("#") {
111119
patternToLineIndex[trimmedLine] = index
120+
} else if index != lines.count - 1 {
121+
nonPatternLines.append((line: line, index: index))
112122
}
113123
}
114124

115-
// Add patterns in the new order specified by the `patterns` array
125+
return (patternToLineIndex, nonPatternLines)
126+
}
127+
128+
private func extractGlobalComments(
129+
_ nonPatternLines: [(line: String, index: Int)],
130+
_ patternToLineIndex: [String: Int]
131+
) -> [String] {
132+
let globalComments = nonPatternLines.filter { $0.index < (patternToLineIndex.values.min() ?? Int.max) }
133+
return globalComments.map(\.line)
134+
}
135+
136+
private func reorderPatterns(
137+
_ globalCommentLines: [String],
138+
_ patternToLineIndex: [String: Int],
139+
_ nonPatternLines: [(line: String, index: Int)],
140+
_ lines: [String]
141+
) -> [String] {
142+
var reorderedLines: [String] = globalCommentLines
143+
var usedNonPatternLines = Set<Int>()
144+
var usedPatterns = Set<String>()
145+
116146
for pattern in patterns {
117147
let value = pattern.value
118-
if let index = patternToLineIndex[value] {
119-
// Keep the original line if it matches a pattern
120-
reorderedLines.append(lines[index])
121-
patternToLineIndex.removeValue(forKey: value)
122-
} else {
123-
// Add new patterns that don't exist in the file
124-
reorderedLines.append(value)
148+
149+
// Insert the pattern
150+
reorderedLines.append(value)
151+
usedPatterns.insert(value)
152+
153+
// Preserve associated non-pattern lines
154+
if let currentIndex = patternToLineIndex[value] {
155+
for nextIndex in (currentIndex + 1)..<lines.count {
156+
if let nonPatternLine = nonPatternLines.first(where: { $0.index == nextIndex }),
157+
!usedNonPatternLines.contains(nonPatternLine.index) {
158+
reorderedLines.append(nonPatternLine.line)
159+
usedNonPatternLines.insert(nonPatternLine.index)
160+
} else {
161+
break
162+
}
163+
}
125164
}
126165
}
127166

128-
// Add remaining non-pattern lines (comments, whitespace)
129-
for (index, line) in lines.enumerated() {
130-
let trimmedLine = line.trimmingCharacters(in: .whitespaces)
131-
if trimmedLine.isEmpty || trimmedLine.hasPrefix("#") {
132-
reorderedLines.insert(line, at: index)
167+
// Retain non-pattern lines that follow deleted patterns
168+
for (line, index) in nonPatternLines {
169+
if !usedNonPatternLines.contains(index) && !reorderedLines.contains(line) {
170+
reorderedLines.append(line)
171+
usedNonPatternLines.insert(index)
133172
}
134173
}
135174

136-
// Ensure single blank line at the end
137-
reorderedLines = cleanUpWhitespace(in: reorderedLines)
175+
// Add new patterns that were not in the original file
176+
for pattern in patterns where !usedPatterns.contains(pattern.value) {
177+
reorderedLines.append(pattern.value)
178+
}
138179

139-
// Write the updated content back to the file
140-
let updatedContent = reorderedLines.joined(separator: "\n")
141-
try? updatedContent.write(to: fileURL, atomically: true, encoding: .utf8)
180+
return reorderedLines
142181
}
143182

144183
private func writeAllPatterns() {

CodeEdit/Features/Settings/Pages/SourceControlSettings/SourceControlGitView.swift

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -135,23 +135,44 @@ private extension SourceControlGitView {
135135
}
136136

137137
private func openGitIgnoreFile() {
138-
let fileURL = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".gitignore_global")
139-
140-
if !FileManager.default.fileExists(atPath: fileURL.path) {
141-
FileManager.default.createFile(atPath: fileURL.path, contents: nil)
142-
guard !FileManager.default.fileExists(atPath: fileURL.path) else { return }
143-
FileManager.default.createFile(atPath: fileURL.path, contents: nil)
144-
Task {
138+
Task {
139+
// Get the `core.excludesfile` configuration
140+
let excludesfile: String? = try await gitConfig.get(key: "core.excludesfile")
141+
142+
// Determine the file URL
143+
let fileURL: URL
144+
if let excludesfile, !excludesfile.isEmpty {
145+
if excludesfile.starts(with: "~/") {
146+
// If the path starts with "~/", expand it to the home directory
147+
let relativePath = String(excludesfile.dropFirst(2)) // Remove "~/"
148+
fileURL = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(relativePath)
149+
} else if excludesfile.starts(with: "/") {
150+
// If the path is absolute, use it directly
151+
fileURL = URL(fileURLWithPath: excludesfile)
152+
} else {
153+
// Assume it's relative to the home directory
154+
fileURL = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(excludesfile)
155+
}
156+
} else {
157+
// Fallback to `.gitignore_global` in the home directory
158+
fileURL = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".gitignore_global")
159+
// Set the default path in Git config if not set
145160
await gitConfig.set(key: "core.excludesfile", value: fileURL.path, global: true)
146161
}
147-
}
148162

149-
NSDocumentController.shared.openDocument(
150-
withContentsOf: fileURL,
151-
display: true
152-
) { _, _, error in
153-
if let error = error {
154-
print("Failed to open document: \(error.localizedDescription)")
163+
// Ensure the file exists
164+
if !FileManager.default.fileExists(atPath: fileURL.path) {
165+
FileManager.default.createFile(atPath: fileURL.path, contents: nil)
166+
}
167+
168+
// Open the file in the editor
169+
NSDocumentController.shared.openDocument(
170+
withContentsOf: fileURL,
171+
display: true
172+
) { _, _, error in
173+
if let error = error {
174+
print("Failed to open document: \(error.localizedDescription)")
175+
}
155176
}
156177
}
157178
}

CodeEdit/Features/Settings/Views/GlobPatternListItem.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ struct GlobPatternListItem: View {
4848
if !value.isEmpty {
4949
if isLast {
5050
addPattern()
51-
} else {
52-
selection.insert(pattern.id)
5351
}
5452
}
5553
}
@@ -58,7 +56,7 @@ struct GlobPatternListItem: View {
5856
if !selection.contains(pattern.id) {
5957
selection = [pattern.id]
6058
}
61-
} else if pattern.value.isEmpty {
59+
} else if value.isEmpty {
6260
removePatterns(selection)
6361
} else if pattern.value != value {
6462
pattern.value = value

0 commit comments

Comments
 (0)