Skip to content

Commit

Permalink
feat(settings): section with glob pattern crud (#1507)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* fix: pr review items

* Update CodeEdit/Features/Settings/Pages/SearchSettings/SearchSettingsView.swift

Co-authored-by: Tom Ludwig <[email protected]>

---------

Co-authored-by: Tom Ludwig <[email protected]>
  • Loading branch information
EstebanBorai and tom-ludwig authored Feb 3, 2024
1 parent a7ee1c2 commit acc3d4b
Show file tree
Hide file tree
Showing 10 changed files with 372 additions and 2 deletions.
36 changes: 36 additions & 0 deletions CodeEdit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@
58F2EB1E292FB954004A9BDE /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 58F2EB1D292FB954004A9BDE /* Sparkle */; };
58FD7608291EA1CB0051D6E4 /* CommandPaletteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD7605291EA1CB0051D6E4 /* CommandPaletteViewModel.swift */; };
58FD7609291EA1CB0051D6E4 /* CommandPaletteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD7607291EA1CB0051D6E4 /* CommandPaletteView.swift */; };
5B241BF32B6DDBFF0016E616 /* IgnorePatternListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B241BF22B6DDBFF0016E616 /* IgnorePatternListItemView.swift */; };
5B698A0A2B262FA000DE9392 /* SearchSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B698A092B262FA000DE9392 /* SearchSettingsView.swift */; };
5B698A0D2B26327800DE9392 /* SearchSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B698A0C2B26327800DE9392 /* SearchSettings.swift */; };
5B698A0F2B2636A700DE9392 /* SearchSettingsIgnoreGlobPatternItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B698A0E2B2636A700DE9392 /* SearchSettingsIgnoreGlobPatternItemView.swift */; };
5B698A162B263BCE00DE9392 /* SearchSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B698A152B263BCE00DE9392 /* SearchSettingsModel.swift */; };
5C4BB1E128212B1E00A92FB2 /* World.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C4BB1E028212B1E00A92FB2 /* World.swift */; };
610C0FDA2B44438F00A01CA7 /* WorkspaceDocument+FindAndReplace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 610C0FD92B44438F00A01CA7 /* WorkspaceDocument+FindAndReplace.swift */; };
611191FA2B08CC9000D4459B /* SearchIndexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 611191F92B08CC9000D4459B /* SearchIndexer.swift */; };
Expand Down Expand Up @@ -797,6 +802,11 @@
58F2EAE1292FB2B0004A9BDE /* SoftwareUpdater.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SoftwareUpdater.swift; sourceTree = "<group>"; };
58FD7605291EA1CB0051D6E4 /* CommandPaletteViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandPaletteViewModel.swift; sourceTree = "<group>"; };
58FD7607291EA1CB0051D6E4 /* CommandPaletteView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandPaletteView.swift; sourceTree = "<group>"; };
5B241BF22B6DDBFF0016E616 /* IgnorePatternListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IgnorePatternListItemView.swift; sourceTree = "<group>"; };
5B698A092B262FA000DE9392 /* SearchSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsView.swift; sourceTree = "<group>"; };
5B698A0C2B26327800DE9392 /* SearchSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettings.swift; sourceTree = "<group>"; };
5B698A0E2B2636A700DE9392 /* SearchSettingsIgnoreGlobPatternItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsIgnoreGlobPatternItemView.swift; sourceTree = "<group>"; };
5B698A152B263BCE00DE9392 /* SearchSettingsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsModel.swift; sourceTree = "<group>"; };
5C4BB1E028212B1E00A92FB2 /* World.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = World.swift; sourceTree = "<group>"; };
610C0FD92B44438F00A01CA7 /* WorkspaceDocument+FindAndReplace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WorkspaceDocument+FindAndReplace.swift"; sourceTree = "<group>"; };
611191F92B08CC9000D4459B /* SearchIndexer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchIndexer.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2256,6 +2266,26 @@
path = Views;
sourceTree = "<group>";
};
5B698A082B262F8400DE9392 /* SearchSettings */ = {
isa = PBXGroup;
children = (
5B698A0B2B26326000DE9392 /* Models */,
5B698A092B262FA000DE9392 /* SearchSettingsView.swift */,
5B241BF22B6DDBFF0016E616 /* IgnorePatternListItemView.swift */,
5B698A0E2B2636A700DE9392 /* SearchSettingsIgnoreGlobPatternItemView.swift */,
);
path = SearchSettings;
sourceTree = "<group>";
};
5B698A0B2B26326000DE9392 /* Models */ = {
isa = PBXGroup;
children = (
5B698A0C2B26327800DE9392 /* SearchSettings.swift */,
5B698A152B263BCE00DE9392 /* SearchSettingsModel.swift */,
);
path = Models;
sourceTree = "<group>";
};
5C403B8D27E20F8000788241 /* Frameworks */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2482,6 +2512,7 @@
B61DA9DD29D929BF00BF4A43 /* Pages */ = {
isa = PBXGroup;
children = (
5B698A082B262F8400DE9392 /* SearchSettings */,
6C5BE51A2A3D5419002DA0FC /* FeatureFlags */,
B6CF632629E5417C0085880A /* Keybindings */,
B6E41C6E29DD15540088F9F4 /* AccountsSettings */,
Expand Down Expand Up @@ -3204,6 +3235,7 @@
587B9E8429301D8F00AC7927 /* GitHubUser.swift in Sources */,
04BA7C1C2AE2D84100584E1C /* GitClient+Commit.swift in Sources */,
B65B10EC2B073913002852CF /* CEContentUnavailableView.swift in Sources */,
5B698A0A2B262FA000DE9392 /* SearchSettingsView.swift in Sources */,
B65B10FB2B08B054002852CF /* Divided.swift in Sources */,
B65B11012B09D5D4002852CF /* GitClient+Pull.swift in Sources */,
2072FA13280D74ED00C7F8D4 /* HistoryInspectorModel.swift in Sources */,
Expand All @@ -3217,6 +3249,7 @@
587B9E9229301D8F00AC7927 /* BitBucketAccount.swift in Sources */,
DE513F52281B672D002260B9 /* EditorTabBarAccessory.swift in Sources */,
2813F93927ECC4C300E305E4 /* NavigatorAreaView.swift in Sources */,
5B698A0F2B2636A700DE9392 /* SearchSettingsIgnoreGlobPatternItemView.swift in Sources */,
587B9E8A29301D8F00AC7927 /* GitHubIssue.swift in Sources */,
EC0870F72A455F6400EB8692 /* ProjectNavigatorViewController+NSMenuDelegate.swift in Sources */,
B60718202B0C6CE7009CDAB4 /* GitStashEntry.swift in Sources */,
Expand Down Expand Up @@ -3460,6 +3493,7 @@
B607184C2B17E037009CDAB4 /* SourceControlStashChangesView.swift in Sources */,
6C14CEB32877A68F001468FE /* FindNavigatorMatchListCell.swift in Sources */,
20EBB501280C325D00F3A5DA /* FileInspectorView.swift in Sources */,
5B698A162B263BCE00DE9392 /* SearchSettingsModel.swift in Sources */,
58822531292C280D00E83CDE /* View+isHovering.swift in Sources */,
587B9E9929301D8F00AC7927 /* GitChangedFile.swift in Sources */,
6C147C4B29A32A7B0089B630 /* Environment+SplitEditor.swift in Sources */,
Expand All @@ -3478,6 +3512,7 @@
6CFF967629BEBCD900182D6F /* FileCommands.swift in Sources */,
B60718462B17DC15009CDAB4 /* RepoOutlineGroupItem.swift in Sources */,
B697937A29FF5668002027EC /* AccountsSettingsAccountLink.swift in Sources */,
5B698A0D2B26327800DE9392 /* SearchSettings.swift in Sources */,
B685DE7929CC9CCD002860C8 /* StatusBarIcon.swift in Sources */,
587B9DA629300ABD00AC7927 /* ToolbarBranchPicker.swift in Sources */,
6C6BD6F629CD145F00235D17 /* ExtensionInfo.swift in Sources */,
Expand Down Expand Up @@ -3591,6 +3626,7 @@
611192002B08CCD700D4459B /* SearchIndexer+Memory.swift in Sources */,
587B9E8129301D8F00AC7927 /* PublicKey.swift in Sources */,
611191FE2B08CCD200D4459B /* SearchIndexer+File.swift in Sources */,
5B241BF32B6DDBFF0016E616 /* IgnorePatternListItemView.swift in Sources */,
6CB52DC92AC8DC3E002E75B3 /* CEWorkspaceFileManager+FileManagement.swift in Sources */,
58F2EB0B292FB2B0004A9BDE /* AccountsSettings.swift in Sources */,
5882252A292C280D00E83CDE /* StatusBarToggleUtilityAreaButton.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git",
"state" : {
"revision" : "43c802fb7f96e090dde015344a94b5e85779eff1",
"version" : "509.1.0"
"revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036",
"version" : "509.0.2"
}
},
{
Expand Down
6 changes: 6 additions & 0 deletions CodeEdit/Features/Settings/Models/SettingsData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ struct SettingsData: Codable, Hashable {
/// Feature Flags settings
var featureFlags: FeatureFlagsSettings = .init()

/// Searh Settings
var search: SearchSettings = .init()

/// Default initializer
init() {}

Expand All @@ -58,6 +61,7 @@ struct SettingsData: Codable, Hashable {
self.theme = try container.decodeIfPresent(ThemeSettings.self, forKey: .theme) ?? .init()
self.terminal = try container.decodeIfPresent(TerminalSettings.self, forKey: .terminal) ?? .init()
self.textEditing = try container.decodeIfPresent(TextEditingSettings.self, forKey: .textEditing) ?? .init()
self.search = try container.decodeIfPresent(SearchSettings.self, forKey: .search) ?? .init()
self.sourceControl = try container.decodeIfPresent(
SourceControlSettings.self,
forKey: .sourceControl
Expand All @@ -84,6 +88,8 @@ struct SettingsData: Codable, Hashable {
textEditing.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) }
case .terminal:
terminal.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) }
case .search:
search.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) }
case .sourceControl:
sourceControl.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) }
case .location:
Expand Down
1 change: 1 addition & 0 deletions CodeEdit/Features/Settings/Models/SettingsPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct SettingsPage: Hashable, Equatable, Identifiable {
case theme = "Themes"
case textEditing = "Text Editing"
case terminal = "Terminal"
case search = "Search"
case keybindings = "Key Bindings"
case sourceControl = "Source Control"
case components = "Components"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// IgnorePatternListItemView.swift
// CodeEdit
//
// Created by Esteban on 2/2/24.
//

import SwiftUI

struct IgnorePatternListItem: View {
@Binding var pattern: GlobPattern
@Binding var selectedPattern: GlobPattern?
var addPattern: () -> Void
var removePattern: (GlobPattern) -> Void
var focusedField: FocusState<String?>.Binding
var isLast: Bool

@State var value: String

@FocusState private var isFocused: Bool

init(
pattern: Binding<GlobPattern>,
selectedPattern: Binding<GlobPattern?>,
addPattern: @escaping () -> Void,
removePattern: @escaping (GlobPattern) -> Void,
focusedField: FocusState<String?>.Binding,
isLast: Bool
) {
self._pattern = pattern
self._selectedPattern = selectedPattern
self.addPattern = addPattern
self.removePattern = removePattern
self.focusedField = focusedField
self.isLast = isLast
self._value = State(initialValue: pattern.wrappedValue.value)
}

var body: some View {
TextField("", text: $value)
.focused(focusedField, equals: pattern.id.uuidString)
.focused($isFocused)
.disableAutocorrection(true)
.autocorrectionDisabled()
.labelsHidden()
.onSubmit {
if !value.isEmpty && isLast {
addPattern()
}
}
.onChange(of: isFocused) { newIsFocused in
if newIsFocused {
if selectedPattern != pattern {
selectedPattern = pattern
}
} else {
if value.isEmpty {
removePattern(pattern)
} else {
pattern.value = value
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// SearchSettings.swift
// CodeEdit
//
// Created by Esteban on 12/10/23.
//

import Foundation

extension SettingsData {
struct SearchSettings: Codable, Hashable, SearchableSettingsPage {

/// The search keys
var searchKeys: [String] {
[
"Ignore Glob Patterns",
"Ignore Patterns"
]
.map { NSLocalizedString($0, comment: "") }
}

/// List of Glob Patterns that determine which files or directories to ignore
var ignoreGlobPatterns: [GlobPattern] = .init()

/// Default initializer
init() {}

/// Explicit decoder init for setting default values when key is not present in `JSON`
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

self.ignoreGlobPatterns = try container.decodeIfPresent(
[GlobPattern].self,
forKey: .ignoreGlobPatterns
) ?? []
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// SearchSettingsModel.swift
// CodeEdit
//
// Created by Esteban on 12/10/23.
//

import SwiftUI

struct GlobPattern: Identifiable, Hashable, Decodable, Encodable {
/// Ephimeral UUID used to track its representation in the UI
var id = UUID()

/// The Glob Pattern to render
var value: String
}

/// The Search Settings View Model. Accessible via the singleton "``SearchSettings/shared``".
///
/// **Usage:**
/// ```swift
/// @StateObject
/// private var searchSettigs: SearchSettingsModel = .shared
/// ```
final class SearchSettingsModel: ObservableObject {
/// Reads settings file for Search Settings and updates the values in this model
/// correspondingly
private init() {
let value = Settings[\.search].ignoreGlobPatterns
self.ignoreGlobPatterns = value
}

static let shared: SearchSettingsModel = .init()

/// Default instance of the `FileManager`
private let filemanager = FileManager.default

/// The base folder url `~/Library/Application Support/CodeEdit/`
private var baseURL: URL {
filemanager.homeDirectoryForCurrentUser.appendingPathComponent("Library/Application Support/CodeEdit")
}

/// The URL of the `search` folder
internal var searchURL: URL {
baseURL.appendingPathComponent("search", isDirectory: true)
}

/// The URL of the `Extensions` folder
internal var extensionsURL: URL {
baseURL.appendingPathComponent("Extensions", isDirectory: true)
}

/// The URL of the `settings.json` file
internal var settingsURL: URL {
baseURL.appendingPathComponent("settings.json", isDirectory: true)
}

/// Stores the new values from the Search Settings Model into the settings.json whenever
/// `ignoreGlobPatterns` is updated
@Published var ignoreGlobPatterns: [GlobPattern] {
didSet {
DispatchQueue.main.async {
Settings[\.search].ignoreGlobPatterns = self.ignoreGlobPatterns
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// SearchSettingsIgnoreGlobPatternItemView.swift
// CodeEdit
//
// Created by Esteban on 12/10/23.
//

import SwiftUI

struct SearchSettingsIgnoreGlobPatternItemView: View {
@Binding var globPattern: String

var body: some View {
Text(globPattern)
}
}
Loading

0 comments on commit acc3d4b

Please sign in to comment.