Skip to content

Commit acc3d4b

Browse files
LeoBoraitom-ludwig
andauthored
feat(settings): section with glob pattern crud (#1507)
* feat: setup search section in app settings * feat: list for ignore glob patterns * feat: open/close modal for add glob pattern * feat: introduce view model for search settings glob pattern * fix: method name * feat: use list to render values * feat: writing to sottings json * feat: persist settings and load from settings * feat: capability to remove selection * fix: editable table lines * fix: fmt * feat: check list size before removing * Update CodeEdit/Features/Settings/Pages/SearchSettings/Models/SearchSettings.swift Co-authored-by: Tom Ludwig <tommludwig@icloud.com> * fix: pr review items * Update CodeEdit/Features/Settings/Pages/SearchSettings/SearchSettingsView.swift Co-authored-by: Tom Ludwig <tommludwig@icloud.com> --------- Co-authored-by: Tom Ludwig <tommludwig@icloud.com>
1 parent a7ee1c2 commit acc3d4b

File tree

10 files changed

+372
-2
lines changed

10 files changed

+372
-2
lines changed

CodeEdit.xcodeproj/project.pbxproj

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,11 @@
247247
58F2EB1E292FB954004A9BDE /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 58F2EB1D292FB954004A9BDE /* Sparkle */; };
248248
58FD7608291EA1CB0051D6E4 /* CommandPaletteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD7605291EA1CB0051D6E4 /* CommandPaletteViewModel.swift */; };
249249
58FD7609291EA1CB0051D6E4 /* CommandPaletteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD7607291EA1CB0051D6E4 /* CommandPaletteView.swift */; };
250+
5B241BF32B6DDBFF0016E616 /* IgnorePatternListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B241BF22B6DDBFF0016E616 /* IgnorePatternListItemView.swift */; };
251+
5B698A0A2B262FA000DE9392 /* SearchSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B698A092B262FA000DE9392 /* SearchSettingsView.swift */; };
252+
5B698A0D2B26327800DE9392 /* SearchSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B698A0C2B26327800DE9392 /* SearchSettings.swift */; };
253+
5B698A0F2B2636A700DE9392 /* SearchSettingsIgnoreGlobPatternItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B698A0E2B2636A700DE9392 /* SearchSettingsIgnoreGlobPatternItemView.swift */; };
254+
5B698A162B263BCE00DE9392 /* SearchSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B698A152B263BCE00DE9392 /* SearchSettingsModel.swift */; };
250255
5C4BB1E128212B1E00A92FB2 /* World.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C4BB1E028212B1E00A92FB2 /* World.swift */; };
251256
610C0FDA2B44438F00A01CA7 /* WorkspaceDocument+FindAndReplace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 610C0FD92B44438F00A01CA7 /* WorkspaceDocument+FindAndReplace.swift */; };
252257
611191FA2B08CC9000D4459B /* SearchIndexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 611191F92B08CC9000D4459B /* SearchIndexer.swift */; };
@@ -797,6 +802,11 @@
797802
58F2EAE1292FB2B0004A9BDE /* SoftwareUpdater.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SoftwareUpdater.swift; sourceTree = "<group>"; };
798803
58FD7605291EA1CB0051D6E4 /* CommandPaletteViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandPaletteViewModel.swift; sourceTree = "<group>"; };
799804
58FD7607291EA1CB0051D6E4 /* CommandPaletteView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandPaletteView.swift; sourceTree = "<group>"; };
805+
5B241BF22B6DDBFF0016E616 /* IgnorePatternListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IgnorePatternListItemView.swift; sourceTree = "<group>"; };
806+
5B698A092B262FA000DE9392 /* SearchSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsView.swift; sourceTree = "<group>"; };
807+
5B698A0C2B26327800DE9392 /* SearchSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettings.swift; sourceTree = "<group>"; };
808+
5B698A0E2B2636A700DE9392 /* SearchSettingsIgnoreGlobPatternItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsIgnoreGlobPatternItemView.swift; sourceTree = "<group>"; };
809+
5B698A152B263BCE00DE9392 /* SearchSettingsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsModel.swift; sourceTree = "<group>"; };
800810
5C4BB1E028212B1E00A92FB2 /* World.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = World.swift; sourceTree = "<group>"; };
801811
610C0FD92B44438F00A01CA7 /* WorkspaceDocument+FindAndReplace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WorkspaceDocument+FindAndReplace.swift"; sourceTree = "<group>"; };
802812
611191F92B08CC9000D4459B /* SearchIndexer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchIndexer.swift; sourceTree = "<group>"; };
@@ -2256,6 +2266,26 @@
22562266
path = Views;
22572267
sourceTree = "<group>";
22582268
};
2269+
5B698A082B262F8400DE9392 /* SearchSettings */ = {
2270+
isa = PBXGroup;
2271+
children = (
2272+
5B698A0B2B26326000DE9392 /* Models */,
2273+
5B698A092B262FA000DE9392 /* SearchSettingsView.swift */,
2274+
5B241BF22B6DDBFF0016E616 /* IgnorePatternListItemView.swift */,
2275+
5B698A0E2B2636A700DE9392 /* SearchSettingsIgnoreGlobPatternItemView.swift */,
2276+
);
2277+
path = SearchSettings;
2278+
sourceTree = "<group>";
2279+
};
2280+
5B698A0B2B26326000DE9392 /* Models */ = {
2281+
isa = PBXGroup;
2282+
children = (
2283+
5B698A0C2B26327800DE9392 /* SearchSettings.swift */,
2284+
5B698A152B263BCE00DE9392 /* SearchSettingsModel.swift */,
2285+
);
2286+
path = Models;
2287+
sourceTree = "<group>";
2288+
};
22592289
5C403B8D27E20F8000788241 /* Frameworks */ = {
22602290
isa = PBXGroup;
22612291
children = (
@@ -2482,6 +2512,7 @@
24822512
B61DA9DD29D929BF00BF4A43 /* Pages */ = {
24832513
isa = PBXGroup;
24842514
children = (
2515+
5B698A082B262F8400DE9392 /* SearchSettings */,
24852516
6C5BE51A2A3D5419002DA0FC /* FeatureFlags */,
24862517
B6CF632629E5417C0085880A /* Keybindings */,
24872518
B6E41C6E29DD15540088F9F4 /* AccountsSettings */,
@@ -3204,6 +3235,7 @@
32043235
587B9E8429301D8F00AC7927 /* GitHubUser.swift in Sources */,
32053236
04BA7C1C2AE2D84100584E1C /* GitClient+Commit.swift in Sources */,
32063237
B65B10EC2B073913002852CF /* CEContentUnavailableView.swift in Sources */,
3238+
5B698A0A2B262FA000DE9392 /* SearchSettingsView.swift in Sources */,
32073239
B65B10FB2B08B054002852CF /* Divided.swift in Sources */,
32083240
B65B11012B09D5D4002852CF /* GitClient+Pull.swift in Sources */,
32093241
2072FA13280D74ED00C7F8D4 /* HistoryInspectorModel.swift in Sources */,
@@ -3217,6 +3249,7 @@
32173249
587B9E9229301D8F00AC7927 /* BitBucketAccount.swift in Sources */,
32183250
DE513F52281B672D002260B9 /* EditorTabBarAccessory.swift in Sources */,
32193251
2813F93927ECC4C300E305E4 /* NavigatorAreaView.swift in Sources */,
3252+
5B698A0F2B2636A700DE9392 /* SearchSettingsIgnoreGlobPatternItemView.swift in Sources */,
32203253
587B9E8A29301D8F00AC7927 /* GitHubIssue.swift in Sources */,
32213254
EC0870F72A455F6400EB8692 /* ProjectNavigatorViewController+NSMenuDelegate.swift in Sources */,
32223255
B60718202B0C6CE7009CDAB4 /* GitStashEntry.swift in Sources */,
@@ -3460,6 +3493,7 @@
34603493
B607184C2B17E037009CDAB4 /* SourceControlStashChangesView.swift in Sources */,
34613494
6C14CEB32877A68F001468FE /* FindNavigatorMatchListCell.swift in Sources */,
34623495
20EBB501280C325D00F3A5DA /* FileInspectorView.swift in Sources */,
3496+
5B698A162B263BCE00DE9392 /* SearchSettingsModel.swift in Sources */,
34633497
58822531292C280D00E83CDE /* View+isHovering.swift in Sources */,
34643498
587B9E9929301D8F00AC7927 /* GitChangedFile.swift in Sources */,
34653499
6C147C4B29A32A7B0089B630 /* Environment+SplitEditor.swift in Sources */,
@@ -3478,6 +3512,7 @@
34783512
6CFF967629BEBCD900182D6F /* FileCommands.swift in Sources */,
34793513
B60718462B17DC15009CDAB4 /* RepoOutlineGroupItem.swift in Sources */,
34803514
B697937A29FF5668002027EC /* AccountsSettingsAccountLink.swift in Sources */,
3515+
5B698A0D2B26327800DE9392 /* SearchSettings.swift in Sources */,
34813516
B685DE7929CC9CCD002860C8 /* StatusBarIcon.swift in Sources */,
34823517
587B9DA629300ABD00AC7927 /* ToolbarBranchPicker.swift in Sources */,
34833518
6C6BD6F629CD145F00235D17 /* ExtensionInfo.swift in Sources */,
@@ -3591,6 +3626,7 @@
35913626
611192002B08CCD700D4459B /* SearchIndexer+Memory.swift in Sources */,
35923627
587B9E8129301D8F00AC7927 /* PublicKey.swift in Sources */,
35933628
611191FE2B08CCD200D4459B /* SearchIndexer+File.swift in Sources */,
3629+
5B241BF32B6DDBFF0016E616 /* IgnorePatternListItemView.swift in Sources */,
35943630
6CB52DC92AC8DC3E002E75B3 /* CEWorkspaceFileManager+FileManagement.swift in Sources */,
35953631
58F2EB0B292FB2B0004A9BDE /* AccountsSettings.swift in Sources */,
35963632
5882252A292C280D00E83CDE /* StatusBarToggleUtilityAreaButton.swift in Sources */,

CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CodeEdit/Features/Settings/Models/SettingsData.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ struct SettingsData: Codable, Hashable {
4747
/// Feature Flags settings
4848
var featureFlags: FeatureFlagsSettings = .init()
4949

50+
/// Searh Settings
51+
var search: SearchSettings = .init()
52+
5053
/// Default initializer
5154
init() {}
5255

@@ -58,6 +61,7 @@ struct SettingsData: Codable, Hashable {
5861
self.theme = try container.decodeIfPresent(ThemeSettings.self, forKey: .theme) ?? .init()
5962
self.terminal = try container.decodeIfPresent(TerminalSettings.self, forKey: .terminal) ?? .init()
6063
self.textEditing = try container.decodeIfPresent(TextEditingSettings.self, forKey: .textEditing) ?? .init()
64+
self.search = try container.decodeIfPresent(SearchSettings.self, forKey: .search) ?? .init()
6165
self.sourceControl = try container.decodeIfPresent(
6266
SourceControlSettings.self,
6367
forKey: .sourceControl
@@ -84,6 +88,8 @@ struct SettingsData: Codable, Hashable {
8488
textEditing.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) }
8589
case .terminal:
8690
terminal.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) }
91+
case .search:
92+
search.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) }
8793
case .sourceControl:
8894
sourceControl.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) }
8995
case .location:

CodeEdit/Features/Settings/Models/SettingsPage.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ struct SettingsPage: Hashable, Equatable, Identifiable {
2626
case theme = "Themes"
2727
case textEditing = "Text Editing"
2828
case terminal = "Terminal"
29+
case search = "Search"
2930
case keybindings = "Key Bindings"
3031
case sourceControl = "Source Control"
3132
case components = "Components"
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// IgnorePatternListItemView.swift
3+
// CodeEdit
4+
//
5+
// Created by Esteban on 2/2/24.
6+
//
7+
8+
import SwiftUI
9+
10+
struct IgnorePatternListItem: View {
11+
@Binding var pattern: GlobPattern
12+
@Binding var selectedPattern: GlobPattern?
13+
var addPattern: () -> Void
14+
var removePattern: (GlobPattern) -> Void
15+
var focusedField: FocusState<String?>.Binding
16+
var isLast: Bool
17+
18+
@State var value: String
19+
20+
@FocusState private var isFocused: Bool
21+
22+
init(
23+
pattern: Binding<GlobPattern>,
24+
selectedPattern: Binding<GlobPattern?>,
25+
addPattern: @escaping () -> Void,
26+
removePattern: @escaping (GlobPattern) -> Void,
27+
focusedField: FocusState<String?>.Binding,
28+
isLast: Bool
29+
) {
30+
self._pattern = pattern
31+
self._selectedPattern = selectedPattern
32+
self.addPattern = addPattern
33+
self.removePattern = removePattern
34+
self.focusedField = focusedField
35+
self.isLast = isLast
36+
self._value = State(initialValue: pattern.wrappedValue.value)
37+
}
38+
39+
var body: some View {
40+
TextField("", text: $value)
41+
.focused(focusedField, equals: pattern.id.uuidString)
42+
.focused($isFocused)
43+
.disableAutocorrection(true)
44+
.autocorrectionDisabled()
45+
.labelsHidden()
46+
.onSubmit {
47+
if !value.isEmpty && isLast {
48+
addPattern()
49+
}
50+
}
51+
.onChange(of: isFocused) { newIsFocused in
52+
if newIsFocused {
53+
if selectedPattern != pattern {
54+
selectedPattern = pattern
55+
}
56+
} else {
57+
if value.isEmpty {
58+
removePattern(pattern)
59+
} else {
60+
pattern.value = value
61+
}
62+
}
63+
}
64+
}
65+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// SearchSettings.swift
3+
// CodeEdit
4+
//
5+
// Created by Esteban on 12/10/23.
6+
//
7+
8+
import Foundation
9+
10+
extension SettingsData {
11+
struct SearchSettings: Codable, Hashable, SearchableSettingsPage {
12+
13+
/// The search keys
14+
var searchKeys: [String] {
15+
[
16+
"Ignore Glob Patterns",
17+
"Ignore Patterns"
18+
]
19+
.map { NSLocalizedString($0, comment: "") }
20+
}
21+
22+
/// List of Glob Patterns that determine which files or directories to ignore
23+
var ignoreGlobPatterns: [GlobPattern] = .init()
24+
25+
/// Default initializer
26+
init() {}
27+
28+
/// Explicit decoder init for setting default values when key is not present in `JSON`
29+
init(from decoder: Decoder) throws {
30+
let container = try decoder.container(keyedBy: CodingKeys.self)
31+
32+
self.ignoreGlobPatterns = try container.decodeIfPresent(
33+
[GlobPattern].self,
34+
forKey: .ignoreGlobPatterns
35+
) ?? []
36+
}
37+
}
38+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//
2+
// SearchSettingsModel.swift
3+
// CodeEdit
4+
//
5+
// Created by Esteban on 12/10/23.
6+
//
7+
8+
import SwiftUI
9+
10+
struct GlobPattern: Identifiable, Hashable, Decodable, Encodable {
11+
/// Ephimeral UUID used to track its representation in the UI
12+
var id = UUID()
13+
14+
/// The Glob Pattern to render
15+
var value: String
16+
}
17+
18+
/// The Search Settings View Model. Accessible via the singleton "``SearchSettings/shared``".
19+
///
20+
/// **Usage:**
21+
/// ```swift
22+
/// @StateObject
23+
/// private var searchSettigs: SearchSettingsModel = .shared
24+
/// ```
25+
final class SearchSettingsModel: ObservableObject {
26+
/// Reads settings file for Search Settings and updates the values in this model
27+
/// correspondingly
28+
private init() {
29+
let value = Settings[\.search].ignoreGlobPatterns
30+
self.ignoreGlobPatterns = value
31+
}
32+
33+
static let shared: SearchSettingsModel = .init()
34+
35+
/// Default instance of the `FileManager`
36+
private let filemanager = FileManager.default
37+
38+
/// The base folder url `~/Library/Application Support/CodeEdit/`
39+
private var baseURL: URL {
40+
filemanager.homeDirectoryForCurrentUser.appendingPathComponent("Library/Application Support/CodeEdit")
41+
}
42+
43+
/// The URL of the `search` folder
44+
internal var searchURL: URL {
45+
baseURL.appendingPathComponent("search", isDirectory: true)
46+
}
47+
48+
/// The URL of the `Extensions` folder
49+
internal var extensionsURL: URL {
50+
baseURL.appendingPathComponent("Extensions", isDirectory: true)
51+
}
52+
53+
/// The URL of the `settings.json` file
54+
internal var settingsURL: URL {
55+
baseURL.appendingPathComponent("settings.json", isDirectory: true)
56+
}
57+
58+
/// Stores the new values from the Search Settings Model into the settings.json whenever
59+
/// `ignoreGlobPatterns` is updated
60+
@Published var ignoreGlobPatterns: [GlobPattern] {
61+
didSet {
62+
DispatchQueue.main.async {
63+
Settings[\.search].ignoreGlobPatterns = self.ignoreGlobPatterns
64+
}
65+
}
66+
}
67+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// SearchSettingsIgnoreGlobPatternItemView.swift
3+
// CodeEdit
4+
//
5+
// Created by Esteban on 12/10/23.
6+
//
7+
8+
import SwiftUI
9+
10+
struct SearchSettingsIgnoreGlobPatternItemView: View {
11+
@Binding var globPattern: String
12+
13+
var body: some View {
14+
Text(globPattern)
15+
}
16+
}

0 commit comments

Comments
 (0)