From b3e8fa4db32b5e716fa439bc2fd274ed5d10c137 Mon Sep 17 00:00:00 2001 From: Ziyuan Zhao Date: Sun, 20 Mar 2022 23:43:16 +0800 Subject: [PATCH 1/8] Basic implement of finding in workspace --- CodeEdit.xcodeproj/project.pbxproj | 20 +++++ .../xcshareddata/swiftpm/Package.resolved | 9 --- .../NavigatorSidebar/NavigatorSidebar.swift | 24 +++--- .../Search/SearchManager.swift | 38 ++++++++++ .../Search/SidebarSearch.swift | 75 +++++++++++++++++++ 5 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 CodeEdit/NavigatorSidebar/Search/SearchManager.swift create mode 100644 CodeEdit/NavigatorSidebar/Search/SidebarSearch.swift diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 7240eddad..2fbdc1ef3 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -42,6 +42,8 @@ B673FDAD27E8296A00795864 /* PressActionsModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = B673FDAC27E8296A00795864 /* PressActionsModifier.swift */; }; B6EE989027E8879A00CDD8AB /* InspectorSidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EE988F27E8879A00CDD8AB /* InspectorSidebar.swift */; }; B6EE989227E887C600CDD8AB /* InspectorSidebarToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EE989127E887C600CDD8AB /* InspectorSidebarToolbar.swift */; }; + D7012EE827E757850001E1EF /* SidebarSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7012EE727E757850001E1EF /* SidebarSearch.swift */; }; + D7012EEA27E75CC70001E1EF /* SearchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7012EE927E75CC70001E1EF /* SearchManager.swift */; }; D70F5E2C27E4E8CF004EE4B9 /* WelcomeModule in Frameworks */ = {isa = PBXBuildFile; productRef = D70F5E2B27E4E8CF004EE4B9 /* WelcomeModule */; }; D7211D4327E066CE008F2ED7 /* Localized+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7211D4227E066CE008F2ED7 /* Localized+Ex.swift */; }; D7211D4727E06BFE008F2ED7 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D7211D4927E06BFE008F2ED7 /* Localizable.strings */; }; @@ -111,6 +113,8 @@ B673FDAC27E8296A00795864 /* PressActionsModifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PressActionsModifier.swift; sourceTree = ""; }; B6EE988F27E8879A00CDD8AB /* InspectorSidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorSidebar.swift; sourceTree = ""; }; B6EE989127E887C600CDD8AB /* InspectorSidebarToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorSidebarToolbar.swift; sourceTree = ""; }; + D7012EE727E757850001E1EF /* SidebarSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarSearch.swift; sourceTree = ""; }; + D7012EE927E75CC70001E1EF /* SearchManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchManager.swift; sourceTree = ""; }; D7211D4227E066CE008F2ED7 /* Localized+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Localized+Ex.swift"; sourceTree = ""; }; D7211D4827E06BFE008F2ED7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; D7211D4A27E06C01008F2ED7 /* sr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sr; path = sr.lproj/Localizable.strings; sourceTree = ""; }; @@ -208,6 +212,11 @@ 287776EC27E350D800D46668 /* NavigatorSidebarItem.swift */, 28B0A19727E385C300B73177 /* NavigatorSidebarToolbarTop.swift */, 28FFE1BE27E3A441001939DB /* NavigatorSidebarToolbarBottom.swift */, + D7012EE627E757660001E1EF /* Search */, + 287776E627E3413200D46668 /* SideBar.swift */, + 287776EC27E350D800D46668 /* SideBarItem.swift */, + 28B0A19727E385C300B73177 /* SideBarToolbarTop.swift */, + 28FFE1BE27E3A441001939DB /* SideBarToolbarBottom.swift */, ); path = NavigatorSidebar; sourceTree = ""; @@ -297,6 +306,15 @@ path = InspectorSidebar; sourceTree = ""; }; + D7012EE627E757660001E1EF /* Search */ = { + isa = PBXGroup; + children = ( + D7012EE727E757850001E1EF /* SidebarSearch.swift */, + D7012EE927E75CC70001E1EF /* SearchManager.swift */, + ); + path = Search; + sourceTree = ""; + }; D7211D4427E066D4008F2ED7 /* Localization */ = { isa = PBXGroup; children = ( @@ -514,6 +532,7 @@ 2B7A583527E4BA0100D25D4E /* AppDelegate.swift in Sources */, 2875A46D27E3BE5B007805F8 /* BreadcrumbsView.swift in Sources */, 0485EB1D27E7338100138301 /* QuickOpenItem.swift in Sources */, + D7012EE827E757850001E1EF /* SidebarSearch.swift in Sources */, 04540D5B27DD08C300E91B77 /* SettingsView.swift in Sources */, D72E1A8927E44D7C00EB11B9 /* WelcomeWindowView.swift in Sources */, 04540D5C27DD08C300E91B77 /* GeneralSettingsView.swift in Sources */, @@ -524,6 +543,7 @@ 04540D5E27DD08C300E91B77 /* WorkspaceView.swift in Sources */, 34EE19BE27E0469C00F152CE /* BlurView.swift in Sources */, D7211D4327E066CE008F2ED7 /* Localized+Ex.swift in Sources */, + D7012EEA27E75CC70001E1EF /* SearchManager.swift in Sources */, 287776E927E34BC700D46668 /* TabBar.swift in Sources */, 0485EB1F27E7458B00138301 /* WorkspaceCodeFileView.swift in Sources */, 0485EB1927E70F4900138301 /* QuickOpenView.swift in Sources */, diff --git a/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7d8fafa3f..8450bfb4c 100644 --- a/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -27,15 +27,6 @@ "revision": "f8a9c997c3c1dab4e216a8ec9014e23144cbab37", "version": "1.9.0" } - }, - { - "package": "Introspect", - "repositoryURL": "https://github.com/siteline/SwiftUI-Introspect", - "state": { - "branch": null, - "revision": "f2616860a41f9d9932da412a8978fec79c06fe24", - "version": "0.1.4" - } } ] }, diff --git a/CodeEdit/NavigatorSidebar/NavigatorSidebar.swift b/CodeEdit/NavigatorSidebar/NavigatorSidebar.swift index e7bc14c12..e37b1f190 100644 --- a/CodeEdit/NavigatorSidebar/NavigatorSidebar.swift +++ b/CodeEdit/NavigatorSidebar/NavigatorSidebar.swift @@ -14,20 +14,24 @@ struct NavigatorSidebar: View { @State private var selection: Int = 0 var body: some View { - List { + ZStack { switch selection { case 0: - Section(header: Text(workspace.fileURL?.lastPathComponent ?? "Unknown")) { - ForEach( - workspace.fileItems.sortItems(foldersOnTop: workspace.sortFoldersOnTop) - ) { item in // Instead of OutlineGroup - NavigatorSidebarItem( - item: item, - workspace: workspace, - windowController: windowController - ) + List { + Section(header: Text(workspace.fileURL?.lastPathComponent ?? "Unknown")) { + ForEach( + workspace.fileItems.sortItems(foldersOnTop: workspace.sortFoldersOnTop) + ) { item in // Instead of OutlineGroup + NavigatorSidebarItem( + item: item, + workspace: workspace, + windowController: windowController + ) + } } } + case 2: + SidebarSearch(workspace: workspace, windowController: windowController) default: EmptyView() } } diff --git a/CodeEdit/NavigatorSidebar/Search/SearchManager.swift b/CodeEdit/NavigatorSidebar/Search/SearchManager.swift new file mode 100644 index 000000000..7d65a1bbb --- /dev/null +++ b/CodeEdit/NavigatorSidebar/Search/SearchManager.swift @@ -0,0 +1,38 @@ +// +// SearchManager.swift +// CodeEdit +// +// Created by Ziyuan Zhao on 2022/3/20. +// + +import Foundation +import WorkspaceClient +import Combine + +class SearchManager: ObservableObject { + + @Published var searchResult: [WorkspaceClient.FileItem: [String]] = [:] + + private var cancellables = Set() + + func search(_ text: String, workspaceClient: WorkspaceClient?) { + searchResult = [:] + workspaceClient? + .getFiles + .sink { [weak self] files in + guard let self = self else { return } + files.forEach { fileItem in + let data = try? String(contentsOf: fileItem.url) + data?.split(separator: "\n").forEach { line in + if line.contains(text) { + var lines = self.searchResult[fileItem] ?? [] + lines.append(String(line)) + self.searchResult[fileItem] = lines + } + } + } + print(self.searchResult) + } + .store(in: &cancellables) + } +} diff --git a/CodeEdit/NavigatorSidebar/Search/SidebarSearch.swift b/CodeEdit/NavigatorSidebar/Search/SidebarSearch.swift new file mode 100644 index 000000000..009666f7e --- /dev/null +++ b/CodeEdit/NavigatorSidebar/Search/SidebarSearch.swift @@ -0,0 +1,75 @@ +// +// SidebarSearch.swift +// CodeEdit +// +// Created by Ziyuan Zhao on 2022/3/20. +// + +import SwiftUI +import WorkspaceClient +import Combine + +struct SidebarSearch: View { + @ObservedObject var workspace: WorkspaceDocument + var windowController: NSWindowController + + @State private var searchText: String = "" + @ObservedObject var searchManger: SearchManager = SearchManager() + + @State var selectedResult: String? + + var body: some View { + VStack { + HStack { + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(Color(nsColor: .secondaryLabelColor)) + TextField("", text: $searchText) + .textFieldStyle(PlainTextFieldStyle()) + if searchText.count > 0 { + Image(systemName: "xmark.circle.fill") + .foregroundColor(Color(nsColor: .secondaryLabelColor)) + } + } + .padding(.vertical, 2) + .padding(.horizontal, 6) + .overlay( + RoundedRectangle(cornerRadius: 4) + .stroke(Color.gray, lineWidth: 1) + ) + } + .padding(.vertical, 4) + .padding(.horizontal, 8) + List(selection: $selectedResult) { + ForEach(Array(searchManger.searchResult.keys), id: \.url) { fileItem in + Section { + ForEach(searchManger.searchResult[fileItem] ?? [], id: \.self) { line in + HStack(alignment: .top) { + Image(systemName: "text.alignleft") + .font(.system(size: 12)) + Text(line) + .lineLimit(Int.max) + .foregroundColor(Color(nsColor: .secondaryLabelColor)) + .font(.system(size: 12, weight: .light)) + } + .padding(.leading, 15) + .tag(line) + } + } header: { + HStack(alignment: .center) { + Image(systemName: fileItem.fileIcon) + .font(.system(size: 13)) + Text(fileItem.fileName) + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(Color(nsColor: NSColor.headerTextColor)) + } + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + } + } + } + } + .onSubmit { + searchManger.search(searchText, workspaceClient: workspace.workspaceClient) + } + } +} From b5000d24190a931499009efccb060830950b84c2 Mon Sep 17 00:00:00 2001 From: Ziyuan Zhao Date: Mon, 21 Mar 2022 02:24:36 +0800 Subject: [PATCH 2/8] Highlight keywords --- .../Search/SearchManager.swift | 41 ++++++++++++++++--- .../Search/SidebarSearch.swift | 6 +-- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/CodeEdit/NavigatorSidebar/Search/SearchManager.swift b/CodeEdit/NavigatorSidebar/Search/SearchManager.swift index 7d65a1bbb..399524fe7 100644 --- a/CodeEdit/NavigatorSidebar/Search/SearchManager.swift +++ b/CodeEdit/NavigatorSidebar/Search/SearchManager.swift @@ -10,9 +10,7 @@ import WorkspaceClient import Combine class SearchManager: ObservableObject { - - @Published var searchResult: [WorkspaceClient.FileItem: [String]] = [:] - + @Published var searchResult: [WorkspaceClient.FileItem: [AttributedString]] = [:] private var cancellables = Set() func search(_ text: String, workspaceClient: WorkspaceClient?) { @@ -25,9 +23,22 @@ class SearchManager: ObservableObject { let data = try? String(contentsOf: fileItem.url) data?.split(separator: "\n").forEach { line in if line.contains(text) { - var lines = self.searchResult[fileItem] ?? [] - lines.append(String(line)) - self.searchResult[fileItem] = lines + line.ranges(of: text).forEach { range in + var attributedString = AttributedString() + attributedString.append( + AttributedString(String(line[line.startIndex..( + of substring: T, + options: String.CompareOptions = [], + locale: Locale? = nil + ) -> [Range] { + var ranges: [Range] = [] + while let result = range( + of: substring, + options: options, + range: (ranges.last?.upperBound ?? startIndex).. Date: Mon, 21 Mar 2022 21:29:28 +0800 Subject: [PATCH 3/8] Support all files matching case search --- CodeEdit.xcodeproj/project.pbxproj | 8 +-- .../xcshareddata/swiftpm/Package.resolved | 9 +++ CodeEdit/Documents/String+Ranges.swift | 26 +++++++++ CodeEdit/Documents/WorkspaceDocument.swift | 55 +++++++++++++++++++ .../NavigatorSidebar/NavigatorSidebar.swift | 2 +- .../Search/SidebarSearch.swift | 32 +++++++---- 6 files changed, 117 insertions(+), 15 deletions(-) create mode 100644 CodeEdit/Documents/String+Ranges.swift diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 2fbdc1ef3..c40ce98f5 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -43,13 +43,13 @@ B6EE989027E8879A00CDD8AB /* InspectorSidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EE988F27E8879A00CDD8AB /* InspectorSidebar.swift */; }; B6EE989227E887C600CDD8AB /* InspectorSidebarToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EE989127E887C600CDD8AB /* InspectorSidebarToolbar.swift */; }; D7012EE827E757850001E1EF /* SidebarSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7012EE727E757850001E1EF /* SidebarSearch.swift */; }; - D7012EEA27E75CC70001E1EF /* SearchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7012EE927E75CC70001E1EF /* SearchManager.swift */; }; D70F5E2C27E4E8CF004EE4B9 /* WelcomeModule in Frameworks */ = {isa = PBXBuildFile; productRef = D70F5E2B27E4E8CF004EE4B9 /* WelcomeModule */; }; D7211D4327E066CE008F2ED7 /* Localized+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7211D4227E066CE008F2ED7 /* Localized+Ex.swift */; }; D7211D4727E06BFE008F2ED7 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D7211D4927E06BFE008F2ED7 /* Localizable.strings */; }; D72E1A8327E3B0D400EB11B9 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72E1A8227E3B0D400EB11B9 /* WelcomeView.swift */; }; D72E1A8727E4242900EB11B9 /* RecentProjectsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72E1A8627E4242900EB11B9 /* RecentProjectsView.swift */; }; D72E1A8927E44D7C00EB11B9 /* WelcomeWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72E1A8827E44D7C00EB11B9 /* WelcomeWindowView.swift */; }; + D7E201AE27E8B3C000CB86D0 /* String+Ranges.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201AD27E8B3C000CB86D0 /* String+Ranges.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -114,13 +114,13 @@ B6EE988F27E8879A00CDD8AB /* InspectorSidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorSidebar.swift; sourceTree = ""; }; B6EE989127E887C600CDD8AB /* InspectorSidebarToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorSidebarToolbar.swift; sourceTree = ""; }; D7012EE727E757850001E1EF /* SidebarSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarSearch.swift; sourceTree = ""; }; - D7012EE927E75CC70001E1EF /* SearchManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchManager.swift; sourceTree = ""; }; D7211D4227E066CE008F2ED7 /* Localized+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Localized+Ex.swift"; sourceTree = ""; }; D7211D4827E06BFE008F2ED7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; D7211D4A27E06C01008F2ED7 /* sr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sr; path = sr.lproj/Localizable.strings; sourceTree = ""; }; D72E1A8227E3B0D400EB11B9 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; D72E1A8627E4242900EB11B9 /* RecentProjectsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentProjectsView.swift; sourceTree = ""; }; D72E1A8827E44D7C00EB11B9 /* WelcomeWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeWindowView.swift; sourceTree = ""; }; + D7E201AD27E8B3C000CB86D0 /* String+Ranges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Ranges.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -162,6 +162,7 @@ 043C321527E3201F006AE443 /* WorkspaceDocument.swift */, 04660F6927E51E5C00477777 /* CodeEditWindowController.swift */, 0485EB1E27E7458B00138301 /* WorkspaceCodeFileView.swift */, + D7E201AD27E8B3C000CB86D0 /* String+Ranges.swift */, ); path = Documents; sourceTree = ""; @@ -310,7 +311,6 @@ isa = PBXGroup; children = ( D7012EE727E757850001E1EF /* SidebarSearch.swift */, - D7012EE927E75CC70001E1EF /* SearchManager.swift */, ); path = Search; sourceTree = ""; @@ -533,6 +533,7 @@ 2875A46D27E3BE5B007805F8 /* BreadcrumbsView.swift in Sources */, 0485EB1D27E7338100138301 /* QuickOpenItem.swift in Sources */, D7012EE827E757850001E1EF /* SidebarSearch.swift in Sources */, + D7E201AE27E8B3C000CB86D0 /* String+Ranges.swift in Sources */, 04540D5B27DD08C300E91B77 /* SettingsView.swift in Sources */, D72E1A8927E44D7C00EB11B9 /* WelcomeWindowView.swift in Sources */, 04540D5C27DD08C300E91B77 /* GeneralSettingsView.swift in Sources */, @@ -543,7 +544,6 @@ 04540D5E27DD08C300E91B77 /* WorkspaceView.swift in Sources */, 34EE19BE27E0469C00F152CE /* BlurView.swift in Sources */, D7211D4327E066CE008F2ED7 /* Localized+Ex.swift in Sources */, - D7012EEA27E75CC70001E1EF /* SearchManager.swift in Sources */, 287776E927E34BC700D46668 /* TabBar.swift in Sources */, 0485EB1F27E7458B00138301 /* WorkspaceCodeFileView.swift in Sources */, 0485EB1927E70F4900138301 /* QuickOpenView.swift in Sources */, diff --git a/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8450bfb4c..7d8fafa3f 100644 --- a/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -27,6 +27,15 @@ "revision": "f8a9c997c3c1dab4e216a8ec9014e23144cbab37", "version": "1.9.0" } + }, + { + "package": "Introspect", + "repositoryURL": "https://github.com/siteline/SwiftUI-Introspect", + "state": { + "branch": null, + "revision": "f2616860a41f9d9932da412a8978fec79c06fe24", + "version": "0.1.4" + } } ] }, diff --git a/CodeEdit/Documents/String+Ranges.swift b/CodeEdit/Documents/String+Ranges.swift new file mode 100644 index 000000000..3da38912f --- /dev/null +++ b/CodeEdit/Documents/String+Ranges.swift @@ -0,0 +1,26 @@ +// +// String+Ranges.swift +// CodeEdit +// +// Created by Ziyuan Zhao on 2022/3/21. +// + +import Foundation + +extension StringProtocol where Index == String.Index { + func ranges( + of substring: T, + options: String.CompareOptions = [], + locale: Locale? = nil + ) -> [Range] { + var ranges: [Range] = [] + while let result = range( + of: substring, + options: options, + range: (ranges.last?.upperBound ?? startIndex)..() @@ -132,6 +133,7 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate { ignoredFilesAndFolders: ignoredFilesAndDirectory ) directoryURL = url + self.searchState = .init(self) self.quickOpenState = .init(self) workspaceClient? .getFiles @@ -173,6 +175,59 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate { } } +// MARK: - Search + +extension WorkspaceDocument { + class SearchState: ObservableObject { + + var workspace: WorkspaceDocument + @Published var searchResult: [URL: [AttributedString]] = [:] + + init(_ workspace: WorkspaceDocument) { + self.workspace = workspace + } + + func search(_ text: String) { + if let url = self.workspace.fileURL { + let enumerator = FileManager.default.enumerator(at: url, + includingPropertiesForKeys: [ + .isRegularFileKey + ], + options: [ + .skipsHiddenFiles, + .skipsPackageDescendants + ]) + if let filePaths = enumerator?.allObjects as? [URL] { + filePaths.forEach { fileURL in + let data = try? String(contentsOf: fileURL) + data?.split(separator: "\n").forEach { line in + let noSpaceLine = line.trimmingCharacters(in: .whitespaces) + if noSpaceLine.contains(text) { + noSpaceLine.ranges(of: text).forEach { range in + var attributedString = AttributedString() + attributedString.append( + AttributedString(String(noSpaceLine[noSpaceLine.startIndex.. 0 { Image(systemName: "xmark.circle.fill") .foregroundColor(Color(nsColor: .secondaryLabelColor)) + .onTapGesture { + searchText = "" + state.search(searchText) + } } } .padding(.vertical, 2) @@ -38,13 +40,22 @@ struct SidebarSearch: View { } .padding(.vertical, 4) .padding(.horizontal, 8) + Divider() + HStack(alignment: .center) { + Text( +"\(Array(state.searchResult.values).flatMap {$0}.count) results in \(Array(state.searchResult.keys).count) files") + .font(.system(size: 10)) +// .foregroundColor(Color(nsColor: )) + } + Divider() List(selection: $selectedResult) { - ForEach(Array(searchManger.searchResult.keys), id: \.url) { fileItem in + ForEach(Array(state.searchResult.keys), id: \.self) { fileURL in Section { - ForEach(searchManger.searchResult[fileItem] ?? [], id: \.self) { line in + ForEach(state.searchResult[fileURL] ?? [], id: \.self) { line in HStack(alignment: .top) { Image(systemName: "text.alignleft") .font(.system(size: 12)) + .padding(.top, 2) Text(line) .lineLimit(Int.max) .foregroundColor(Color(nsColor: .secondaryLabelColor)) @@ -55,11 +66,12 @@ struct SidebarSearch: View { } } header: { HStack(alignment: .center) { - Image(systemName: fileItem.fileIcon) - .font(.system(size: 13)) - Text(fileItem.fileName) +// Image(nsImage: NSWorkspace.shared.icon(forFile: fileURL.path)) +// .frame(width: 13, height: 13) + Text(fileURL.lastPathComponent) .font(.system(size: 13, weight: .semibold)) .foregroundColor(Color(nsColor: NSColor.headerTextColor)) + Text(fileURL.path.replacingOccurrences(of: state.workspace.fileURL?.path ?? "", with: "")) } .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) } @@ -67,7 +79,7 @@ struct SidebarSearch: View { } } .onSubmit { - searchManger.search(searchText, workspaceClient: workspace.workspaceClient) + state.search(searchText) } } } From b5d9a03693103c8f01f7c69e7aa3388a38de5d4c Mon Sep 17 00:00:00 2001 From: Ziyuan Zhao Date: Tue, 22 Mar 2022 13:47:16 +0800 Subject: [PATCH 4/8] Search mode menu UI --- CodeEdit.xcodeproj/project.pbxproj | 14 ++ .../xcshareddata/swiftpm/Package.resolved | 9 - .../Search/FindResultList.swift | 45 +++++ .../NavigatorSidebar/Search/SearchBar.swift | 50 +++++ .../Search/SearchModeSelector.swift | 173 ++++++++++++++++++ .../Search/SidebarSearch.swift | 59 +----- 6 files changed, 290 insertions(+), 60 deletions(-) create mode 100644 CodeEdit/NavigatorSidebar/Search/FindResultList.swift create mode 100644 CodeEdit/NavigatorSidebar/Search/SearchBar.swift create mode 100644 CodeEdit/NavigatorSidebar/Search/SearchModeSelector.swift diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index c40ce98f5..11a64c6b9 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -50,6 +50,9 @@ D72E1A8727E4242900EB11B9 /* RecentProjectsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72E1A8627E4242900EB11B9 /* RecentProjectsView.swift */; }; D72E1A8927E44D7C00EB11B9 /* WelcomeWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72E1A8827E44D7C00EB11B9 /* WelcomeWindowView.swift */; }; D7E201AE27E8B3C000CB86D0 /* String+Ranges.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201AD27E8B3C000CB86D0 /* String+Ranges.swift */; }; + D7E201B027E8C07300CB86D0 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201AF27E8C07300CB86D0 /* SearchBar.swift */; }; + D7E201B227E8D50000CB86D0 /* SearchModeSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201B127E8D50000CB86D0 /* SearchModeSelector.swift */; }; + D7E201B427E9989900CB86D0 /* FindResultList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201B327E9989900CB86D0 /* FindResultList.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -121,6 +124,9 @@ D72E1A8627E4242900EB11B9 /* RecentProjectsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentProjectsView.swift; sourceTree = ""; }; D72E1A8827E44D7C00EB11B9 /* WelcomeWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeWindowView.swift; sourceTree = ""; }; D7E201AD27E8B3C000CB86D0 /* String+Ranges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Ranges.swift"; sourceTree = ""; }; + D7E201AF27E8C07300CB86D0 /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = ""; }; + D7E201B127E8D50000CB86D0 /* SearchModeSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModeSelector.swift; sourceTree = ""; }; + D7E201B327E9989900CB86D0 /* FindResultList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindResultList.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -311,6 +317,9 @@ isa = PBXGroup; children = ( D7012EE727E757850001E1EF /* SidebarSearch.swift */, + D7E201AF27E8C07300CB86D0 /* SearchBar.swift */, + D7E201B127E8D50000CB86D0 /* SearchModeSelector.swift */, + D7E201B327E9989900CB86D0 /* FindResultList.swift */, ); path = Search; sourceTree = ""; @@ -535,6 +544,7 @@ D7012EE827E757850001E1EF /* SidebarSearch.swift in Sources */, D7E201AE27E8B3C000CB86D0 /* String+Ranges.swift in Sources */, 04540D5B27DD08C300E91B77 /* SettingsView.swift in Sources */, + D7E201B027E8C07300CB86D0 /* SearchBar.swift in Sources */, D72E1A8927E44D7C00EB11B9 /* WelcomeWindowView.swift in Sources */, 04540D5C27DD08C300E91B77 /* GeneralSettingsView.swift in Sources */, B6EE989227E887C600CDD8AB /* InspectorSidebarToolbar.swift in Sources */, @@ -544,6 +554,7 @@ 04540D5E27DD08C300E91B77 /* WorkspaceView.swift in Sources */, 34EE19BE27E0469C00F152CE /* BlurView.swift in Sources */, D7211D4327E066CE008F2ED7 /* Localized+Ex.swift in Sources */, + D7E201B227E8D50000CB86D0 /* SearchModeSelector.swift in Sources */, 287776E927E34BC700D46668 /* TabBar.swift in Sources */, 0485EB1F27E7458B00138301 /* WorkspaceCodeFileView.swift in Sources */, 0485EB1927E70F4900138301 /* QuickOpenView.swift in Sources */, @@ -554,6 +565,9 @@ 287776E727E3413200D46668 /* NavigatorSidebar.swift in Sources */, 287776ED27E350D800D46668 /* NavigatorSidebarItem.swift in Sources */, B6EE989027E8879A00CDD8AB /* InspectorSidebar.swift in Sources */, + D7E201B427E9989900CB86D0 /* FindResultList.swift in Sources */, + 287776E727E3413200D46668 /* SideBar.swift in Sources */, + 287776ED27E350D800D46668 /* SideBarItem.swift in Sources */, 289978ED27E4E97E00BB0357 /* FileIconStyle.swift in Sources */, 04660F6627E3ACEF00477777 /* ReopenBehavior.swift in Sources */, D72E1A8327E3B0D400EB11B9 /* WelcomeView.swift in Sources */, diff --git a/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7d8fafa3f..8450bfb4c 100644 --- a/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -27,15 +27,6 @@ "revision": "f8a9c997c3c1dab4e216a8ec9014e23144cbab37", "version": "1.9.0" } - }, - { - "package": "Introspect", - "repositoryURL": "https://github.com/siteline/SwiftUI-Introspect", - "state": { - "branch": null, - "revision": "f2616860a41f9d9932da412a8978fec79c06fe24", - "version": "0.1.4" - } } ] }, diff --git a/CodeEdit/NavigatorSidebar/Search/FindResultList.swift b/CodeEdit/NavigatorSidebar/Search/FindResultList.swift new file mode 100644 index 000000000..bc6b52030 --- /dev/null +++ b/CodeEdit/NavigatorSidebar/Search/FindResultList.swift @@ -0,0 +1,45 @@ +// +// FindResultList.swift +// CodeEdit +// +// Created by Ziyuan Zhao on 2022/3/22. +// + +import SwiftUI + +struct FindResultList: View { + @ObservedObject var state: WorkspaceDocument.SearchState + @State var selectedResult: AttributedString? + + var body: some View { + List(selection: $selectedResult) { + ForEach(Array(state.searchResult.keys), id: \.self) { fileURL in + Section { + ForEach(state.searchResult[fileURL] ?? [], id: \.self) { line in + HStack(alignment: .top) { + Image(systemName: "text.alignleft") + .font(.system(size: 12)) + .padding(.top, 2) + Text(line) + .lineLimit(Int.max) + .foregroundColor(Color(nsColor: .secondaryLabelColor)) + .font(.system(size: 12, weight: .light)) + } + .padding(.leading, 15) + .tag(line) + } + } header: { + HStack(alignment: .center) { +// Image(nsImage: NSWorkspace.shared.icon(forFile: fileURL.path)) +// .frame(width: 13, height: 13) + Text(fileURL.lastPathComponent) + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(Color(nsColor: NSColor.headerTextColor)) + Text(fileURL.path.replacingOccurrences(of: state.workspace.fileURL?.path ?? "", with: "")) + } + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + } + } + } + } +} diff --git a/CodeEdit/NavigatorSidebar/Search/SearchBar.swift b/CodeEdit/NavigatorSidebar/Search/SearchBar.swift new file mode 100644 index 000000000..85ce51e1a --- /dev/null +++ b/CodeEdit/NavigatorSidebar/Search/SearchBar.swift @@ -0,0 +1,50 @@ +// +// SearchBar.swift +// CodeEdit +// +// Created by Ziyuan Zhao on 2022/3/21. +// + +import SwiftUI + +struct SearchBar: View { + let title: String + @Binding var text: String + + var body: some View { + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(Color(nsColor: .secondaryLabelColor)) + textField + if !text.isEmpty { clearButton } + } + .padding(.horizontal, 5) + .padding(.vertical, 3) + .overlay(RoundedRectangle(cornerRadius: 4).stroke(Color.gray, lineWidth: 0.5).cornerRadius(4)) + } + + private var textField: some View { + TextField(title, text: $text) + .disableAutocorrection(true) + .textFieldStyle(PlainTextFieldStyle()) + } + + private var clearButton: some View { + Button { + self.text = "" + } label: { + Image(systemName: "xmark.circle.fill") + } + .foregroundColor(.secondary) + .buttonStyle(PlainButtonStyle()) + } +} + +struct SearchBar_Previews: PreviewProvider { + static var previews: some View { + HStack { + SearchBar(title: "placeholder", text: .constant("value")) + } + .padding() + } +} diff --git a/CodeEdit/NavigatorSidebar/Search/SearchModeSelector.swift b/CodeEdit/NavigatorSidebar/Search/SearchModeSelector.swift new file mode 100644 index 000000000..cca7dc70c --- /dev/null +++ b/CodeEdit/NavigatorSidebar/Search/SearchModeSelector.swift @@ -0,0 +1,173 @@ +// +// SearchModeSelector.swift +// CodeEdit +// +// Created by Ziyuan Zhao on 2022/3/21. +// + +import SwiftUI + +struct SearchMode { + let title: String + let children: [SearchMode] + let needSelectionHightlight: Bool + + static let Containing = SearchMode(title: "Containing", children: [], needSelectionHightlight: false) + static let MatchingWord = SearchMode(title: "Matching Word", children: [], needSelectionHightlight: true) + static let StartingWith = SearchMode(title: "Starting With", children: [], needSelectionHightlight: true) + static let EndingWith = SearchMode(title: "Ending With", children: [], needSelectionHightlight: true) + + static let Text = SearchMode(title: "Text", children: [.Containing, .MatchingWord, .StartingWith, .EndingWith], needSelectionHightlight: false) + static let References = SearchMode(title: "References", children: [.Containing, .MatchingWord, .StartingWith, .EndingWith], needSelectionHightlight: true) + static let Definitions = SearchMode(title: "Definitions", children: [.Containing, .MatchingWord, .StartingWith, .EndingWith], needSelectionHightlight: true) + static let RegularExpression = SearchMode(title: "Regular Expression", children: [], needSelectionHightlight: true) + static let CallHierarchy = SearchMode(title: "Call Hierarchy", children: [], needSelectionHightlight: true) + + static let Find = SearchMode(title: "Find", children: [.Text, .References, .Definitions, .RegularExpression, .CallHierarchy], needSelectionHightlight: false) + static let Replace = SearchMode(title: "Replace", children: [.Text, .RegularExpression], needSelectionHightlight: true) + + static let Search = SearchMode(title: "", children: [.Find, .Replace], needSelectionHightlight: false) + + static let TextMatchingModes: [SearchMode] = [.Containing, .MatchingWord, .StartingWith, .EndingWith] + static let FindModes: [SearchMode] = [.Text, .References, .Definitions, .RegularExpression, .CallHierarchy] + static let ReplaceModes: [SearchMode] = [.Text, .RegularExpression] + static let SearchModes: [SearchMode] = [.Find, .Replace] + + static func getAllModes(_ index: Int, currentSelected: [SearchMode]) -> [SearchMode] { + switch index { + case 0: + return SearchModes + case 1: + if let searchMode = currentSelected.first { + if searchMode == SearchMode.Find { + return FindModes + } else if searchMode == searchMode { + return ReplaceModes + } + } else { + return [] + } + case 2: + return TextMatchingModes + default: + return [] + } + return [] + } +} + +extension SearchMode: Equatable { + static func ==(lhs: SearchMode, rhs: SearchMode) -> Bool { + return lhs.title == rhs.title && lhs.children == rhs.children && lhs.needSelectionHightlight == rhs.needSelectionHightlight + } +} + +struct SearchModeSelector: View { + + @State var selectedMode: [SearchMode] = [ + .Find, + .Text, + .Containing + ] + + private func getMenuList(_ index: Int) -> [SearchMode] { + return index == 0 ? SearchMode.SearchModes : selectedMode[index - 1].children + } + + private func onSelectMenuItem(_ index: Int, searchMode: SearchMode) { + var newSelectedMode: [SearchMode] = [] + switch index { + case 0: + newSelectedMode.append(searchMode) + if let secondMode = searchMode.children.first { + if let selectedSecondMode = selectedMode.second, searchMode.children.contains(selectedSecondMode) { + newSelectedMode.append(contentsOf: selectedMode.dropFirst()) + } else { + newSelectedMode.append(secondMode) + if let thirdMode = secondMode.children.first, let selectedThirdMode = selectedMode.third { + if (secondMode.children.contains(selectedThirdMode)) { + newSelectedMode.append(selectedThirdMode) + } else { + newSelectedMode.append(thirdMode) + } + } + } + } + self.selectedMode = newSelectedMode + case 1: + if let firstMode = selectedMode.first { + newSelectedMode.append(contentsOf: [firstMode, searchMode]) + if let thirdMode = searchMode.children.first { + if let selectedThirdMode = selectedMode.third, (searchMode.children.contains(selectedThirdMode)) { + newSelectedMode.append(selectedThirdMode) + } else { + newSelectedMode.append(thirdMode) + } + } + } + self.selectedMode = newSelectedMode + case 2: + if let firstMode = selectedMode.first, let secondMode = selectedMode.second { + newSelectedMode.append(contentsOf: [firstMode, secondMode, searchMode]) + } + self.selectedMode = newSelectedMode + default: + return + } + } + + private var chevron: some View { + Image(systemName: "chevron.compact.right") + .foregroundStyle(.secondary) + .imageScale(.large) + } + + var body: some View { + HStack(spacing: 0) { + ForEach(0.. 0 { + chevron + } + Text(selectedMode[index].title) + .foregroundColor(selectedMode[index].needSelectionHightlight ? Color.accentColor : .primary) + .font(.system(size: 10)) + } + } + .searchModeMenu() + } + Spacer() + } + } +} + +extension Array { + var second: Element? { + return self.count > 1 ? self[1] : nil + } + + var third: Element? { + return self.count > 2 ? self[2] : nil + } +} + +extension View { + func searchModeMenu() -> some View { + menuStyle(.borderlessButton) + .fixedSize() + .menuIndicator(.hidden) + } +} + +struct SearchModeSelector_Previews: PreviewProvider { + static var previews: some View { + SearchModeSelector() + } +} diff --git a/CodeEdit/NavigatorSidebar/Search/SidebarSearch.swift b/CodeEdit/NavigatorSidebar/Search/SidebarSearch.swift index 6a6ba212d..20d7663e0 100644 --- a/CodeEdit/NavigatorSidebar/Search/SidebarSearch.swift +++ b/CodeEdit/NavigatorSidebar/Search/SidebarSearch.swift @@ -12,34 +12,19 @@ import Combine struct SidebarSearch: View { @ObservedObject var state: WorkspaceDocument.SearchState @State private var searchText: String = "" - @State var selectedResult: AttributedString? var body: some View { VStack { - HStack { + VStack { + SearchModeSelector() + SearchBar(title: "", text: $searchText) HStack { - Image(systemName: "magnifyingglass") - .foregroundColor(Color(nsColor: .secondaryLabelColor)) - TextField("", text: $searchText) - .textFieldStyle(PlainTextFieldStyle()) - if searchText.count > 0 { - Image(systemName: "xmark.circle.fill") - .foregroundColor(Color(nsColor: .secondaryLabelColor)) - .onTapGesture { - searchText = "" - state.search(searchText) - } - } + Spacer() + } - .padding(.vertical, 2) - .padding(.horizontal, 6) - .overlay( - RoundedRectangle(cornerRadius: 4) - .stroke(Color.gray, lineWidth: 1) - ) } - .padding(.vertical, 4) - .padding(.horizontal, 8) + .padding(.horizontal, 10) + .padding(.vertical, 5) Divider() HStack(alignment: .center) { Text( @@ -48,35 +33,7 @@ struct SidebarSearch: View { // .foregroundColor(Color(nsColor: )) } Divider() - List(selection: $selectedResult) { - ForEach(Array(state.searchResult.keys), id: \.self) { fileURL in - Section { - ForEach(state.searchResult[fileURL] ?? [], id: \.self) { line in - HStack(alignment: .top) { - Image(systemName: "text.alignleft") - .font(.system(size: 12)) - .padding(.top, 2) - Text(line) - .lineLimit(Int.max) - .foregroundColor(Color(nsColor: .secondaryLabelColor)) - .font(.system(size: 12, weight: .light)) - } - .padding(.leading, 15) - .tag(line) - } - } header: { - HStack(alignment: .center) { -// Image(nsImage: NSWorkspace.shared.icon(forFile: fileURL.path)) -// .frame(width: 13, height: 13) - Text(fileURL.lastPathComponent) - .font(.system(size: 13, weight: .semibold)) - .foregroundColor(Color(nsColor: NSColor.headerTextColor)) - Text(fileURL.path.replacingOccurrences(of: state.workspace.fileURL?.path ?? "", with: "")) - } - .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) - } - } - } + FindResultList(state: state) } .onSubmit { state.search(searchText) From 81a42f76c8d4c3f44fc3ffeff00085f8ea1c9850 Mon Sep 17 00:00:00 2001 From: Ziyuan Zhao Date: Tue, 22 Mar 2022 13:55:35 +0800 Subject: [PATCH 5/8] Support open file from search result list --- CodeEdit/Documents/WorkspaceDocument.swift | 12 +++++++----- .../NavigatorSidebar/Search/FindResultList.swift | 12 ++++++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/CodeEdit/Documents/WorkspaceDocument.swift b/CodeEdit/Documents/WorkspaceDocument.swift index 0fb741775..6a1ed1c04 100644 --- a/CodeEdit/Documents/WorkspaceDocument.swift +++ b/CodeEdit/Documents/WorkspaceDocument.swift @@ -181,7 +181,7 @@ extension WorkspaceDocument { class SearchState: ObservableObject { var workspace: WorkspaceDocument - @Published var searchResult: [URL: [AttributedString]] = [:] + @Published var searchResult: [WorkspaceClient.FileItem: [AttributedString]] = [:] init(_ workspace: WorkspaceDocument) { self.workspace = workspace @@ -198,8 +198,10 @@ extension WorkspaceDocument { .skipsPackageDescendants ]) if let filePaths = enumerator?.allObjects as? [URL] { - filePaths.forEach { fileURL in - let data = try? String(contentsOf: fileURL) + filePaths.map { url in + WorkspaceClient.FileItem(url: url, children: nil) + }.forEach { fileItem in + let data = try? String(contentsOf: fileItem.url) data?.split(separator: "\n").forEach { line in let noSpaceLine = line.trimmingCharacters(in: .whitespaces) if noSpaceLine.contains(text) { @@ -215,9 +217,9 @@ extension WorkspaceDocument { attributedString.append( AttributedString(String(noSpaceLine[range.upperBound.. Date: Tue, 22 Mar 2022 23:24:56 +0800 Subject: [PATCH 6/8] Improve codes --- CodeEdit.xcodeproj/project.pbxproj | 33 +++++-- CodeEdit/Documents/WorkspaceDocument.swift | 50 ++++++----- .../Search/Model/FindModeModel.swift | 75 ++++++++++++++++ .../Search/Model/SearchResultModel.swift | 20 +++++ .../NavigatorSidebar/Search/SearchBar.swift | 4 +- .../Search/SearchModeSelector.swift | 85 ++++--------------- .../Search/SearchResultFileItem.swift | 57 +++++++++++++ .../Search/SearchResultList.swift | 38 +++++++++ .../NavigatorSidebar/Search/SidebarFind.swift | 48 +++++++++++ 9 files changed, 314 insertions(+), 96 deletions(-) create mode 100644 CodeEdit/NavigatorSidebar/Search/Model/FindModeModel.swift create mode 100644 CodeEdit/NavigatorSidebar/Search/Model/SearchResultModel.swift create mode 100644 CodeEdit/NavigatorSidebar/Search/SearchResultFileItem.swift create mode 100644 CodeEdit/NavigatorSidebar/Search/SearchResultList.swift create mode 100644 CodeEdit/NavigatorSidebar/Search/SidebarFind.swift diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 11a64c6b9..6d98c7284 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ B6EE989027E8879A00CDD8AB /* InspectorSidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EE988F27E8879A00CDD8AB /* InspectorSidebar.swift */; }; B6EE989227E887C600CDD8AB /* InspectorSidebarToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EE989127E887C600CDD8AB /* InspectorSidebarToolbar.swift */; }; D7012EE827E757850001E1EF /* SidebarSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7012EE727E757850001E1EF /* SidebarSearch.swift */; }; + D7012EE827E757850001E1EF /* SidebarFind.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7012EE727E757850001E1EF /* SidebarFind.swift */; }; D70F5E2C27E4E8CF004EE4B9 /* WelcomeModule in Frameworks */ = {isa = PBXBuildFile; productRef = D70F5E2B27E4E8CF004EE4B9 /* WelcomeModule */; }; D7211D4327E066CE008F2ED7 /* Localized+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7211D4227E066CE008F2ED7 /* Localized+Ex.swift */; }; D7211D4727E06BFE008F2ED7 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D7211D4927E06BFE008F2ED7 /* Localizable.strings */; }; @@ -52,7 +53,10 @@ D7E201AE27E8B3C000CB86D0 /* String+Ranges.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201AD27E8B3C000CB86D0 /* String+Ranges.swift */; }; D7E201B027E8C07300CB86D0 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201AF27E8C07300CB86D0 /* SearchBar.swift */; }; D7E201B227E8D50000CB86D0 /* SearchModeSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201B127E8D50000CB86D0 /* SearchModeSelector.swift */; }; - D7E201B427E9989900CB86D0 /* FindResultList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201B327E9989900CB86D0 /* FindResultList.swift */; }; + D7E201B427E9989900CB86D0 /* SearchResultList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201B327E9989900CB86D0 /* SearchResultList.swift */; }; + D7E201BB27E9ECD600CB86D0 /* SearchResultModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201BA27E9ECD600CB86D0 /* SearchResultModel.swift */; }; + D7E201BD27EA00E200CB86D0 /* SearchResultFileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201BC27EA00E200CB86D0 /* SearchResultFileItem.swift */; }; + D7E201BF27EA1ACD00CB86D0 /* FindModeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201BE27EA1ACD00CB86D0 /* FindModeModel.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -117,6 +121,7 @@ B6EE988F27E8879A00CDD8AB /* InspectorSidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorSidebar.swift; sourceTree = ""; }; B6EE989127E887C600CDD8AB /* InspectorSidebarToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorSidebarToolbar.swift; sourceTree = ""; }; D7012EE727E757850001E1EF /* SidebarSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarSearch.swift; sourceTree = ""; }; + D7012EE727E757850001E1EF /* SidebarFind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarFind.swift; sourceTree = ""; }; D7211D4227E066CE008F2ED7 /* Localized+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Localized+Ex.swift"; sourceTree = ""; }; D7211D4827E06BFE008F2ED7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; D7211D4A27E06C01008F2ED7 /* sr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sr; path = sr.lproj/Localizable.strings; sourceTree = ""; }; @@ -126,7 +131,10 @@ D7E201AD27E8B3C000CB86D0 /* String+Ranges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Ranges.swift"; sourceTree = ""; }; D7E201AF27E8C07300CB86D0 /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = ""; }; D7E201B127E8D50000CB86D0 /* SearchModeSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModeSelector.swift; sourceTree = ""; }; - D7E201B327E9989900CB86D0 /* FindResultList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindResultList.swift; sourceTree = ""; }; + D7E201B327E9989900CB86D0 /* SearchResultList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultList.swift; sourceTree = ""; }; + D7E201BA27E9ECD600CB86D0 /* SearchResultModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultModel.swift; sourceTree = ""; }; + D7E201BC27EA00E200CB86D0 /* SearchResultFileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultFileItem.swift; sourceTree = ""; }; + D7E201BE27EA1ACD00CB86D0 /* FindModeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindModeModel.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -316,10 +324,12 @@ D7012EE627E757660001E1EF /* Search */ = { isa = PBXGroup; children = ( - D7012EE727E757850001E1EF /* SidebarSearch.swift */, + D7E201B527E9EA8A00CB86D0 /* Model */, + D7012EE727E757850001E1EF /* SidebarFind.swift */, D7E201AF27E8C07300CB86D0 /* SearchBar.swift */, D7E201B127E8D50000CB86D0 /* SearchModeSelector.swift */, - D7E201B327E9989900CB86D0 /* FindResultList.swift */, + D7E201B327E9989900CB86D0 /* SearchResultList.swift */, + D7E201BC27EA00E200CB86D0 /* SearchResultFileItem.swift */, ); path = Search; sourceTree = ""; @@ -343,6 +353,15 @@ path = Welcome; sourceTree = ""; }; + D7E201B527E9EA8A00CB86D0 /* Model */ = { + isa = PBXGroup; + children = ( + D7E201BA27E9ECD600CB86D0 /* SearchResultModel.swift */, + D7E201BE27EA1ACD00CB86D0 /* FindModeModel.swift */, + ); + path = Model; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -541,7 +560,8 @@ 2B7A583527E4BA0100D25D4E /* AppDelegate.swift in Sources */, 2875A46D27E3BE5B007805F8 /* BreadcrumbsView.swift in Sources */, 0485EB1D27E7338100138301 /* QuickOpenItem.swift in Sources */, - D7012EE827E757850001E1EF /* SidebarSearch.swift in Sources */, + D7E201BB27E9ECD600CB86D0 /* SearchResultModel.swift in Sources */, + D7012EE827E757850001E1EF /* SidebarFind.swift in Sources */, D7E201AE27E8B3C000CB86D0 /* String+Ranges.swift in Sources */, 04540D5B27DD08C300E91B77 /* SettingsView.swift in Sources */, D7E201B027E8C07300CB86D0 /* SearchBar.swift in Sources */, @@ -558,6 +578,7 @@ 287776E927E34BC700D46668 /* TabBar.swift in Sources */, 0485EB1F27E7458B00138301 /* WorkspaceCodeFileView.swift in Sources */, 0485EB1927E70F4900138301 /* QuickOpenView.swift in Sources */, + D7E201BD27EA00E200CB86D0 /* SearchResultFileItem.swift in Sources */, 286620A527E4AB6900E18C2B /* BreadcrumbsComponent.swift in Sources */, 287776EF27E3515300D46668 /* TabBarItem.swift in Sources */, D72E1A8727E4242900EB11B9 /* RecentProjectsView.swift in Sources */, @@ -566,6 +587,8 @@ 287776ED27E350D800D46668 /* NavigatorSidebarItem.swift in Sources */, B6EE989027E8879A00CDD8AB /* InspectorSidebar.swift in Sources */, D7E201B427E9989900CB86D0 /* FindResultList.swift in Sources */, + D7E201BF27EA1ACD00CB86D0 /* FindModeModel.swift in Sources */, + D7E201B427E9989900CB86D0 /* SearchResultList.swift in Sources */, 287776E727E3413200D46668 /* SideBar.swift in Sources */, 287776ED27E350D800D46668 /* SideBarItem.swift in Sources */, 289978ED27E4E97E00BB0357 /* FileIconStyle.swift in Sources */, diff --git a/CodeEdit/Documents/WorkspaceDocument.swift b/CodeEdit/Documents/WorkspaceDocument.swift index 6a1ed1c04..d90182776 100644 --- a/CodeEdit/Documents/WorkspaceDocument.swift +++ b/CodeEdit/Documents/WorkspaceDocument.swift @@ -181,13 +181,14 @@ extension WorkspaceDocument { class SearchState: ObservableObject { var workspace: WorkspaceDocument - @Published var searchResult: [WorkspaceClient.FileItem: [AttributedString]] = [:] + @Published var searchResult: [SearchResultModel] = [] init(_ workspace: WorkspaceDocument) { self.workspace = workspace } func search(_ text: String) { + self.searchResult = [] if let url = self.workspace.fileURL { let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: [ @@ -201,28 +202,35 @@ extension WorkspaceDocument { filePaths.map { url in WorkspaceClient.FileItem(url: url, children: nil) }.forEach { fileItem in - let data = try? String(contentsOf: fileItem.url) - data?.split(separator: "\n").forEach { line in - let noSpaceLine = line.trimmingCharacters(in: .whitespaces) - if noSpaceLine.contains(text) { - noSpaceLine.ranges(of: text).forEach { range in - var attributedString = AttributedString() - attributedString.append( - AttributedString(String(noSpaceLine[noSpaceLine.startIndex.. [SearchModeModel] { + switch index { + case 0: + return SearchModes + case 1: + if let searchMode = currentSelected.first { + if searchMode == SearchModeModel.Find { + return SearchModes + } else if searchMode == searchMode { + return ReplaceModes + } + } else { + return [] + } + case 2: + return TextMatchingModes + default: + return [] + } + return [] + } +} + +extension SearchModeModel: Equatable { + static func == (lhs: SearchModeModel, rhs: SearchModeModel) -> Bool { + return lhs.title == rhs.title + && lhs.children == rhs.children + && lhs.needSelectionHightlight == rhs.needSelectionHightlight + } +} diff --git a/CodeEdit/NavigatorSidebar/Search/Model/SearchResultModel.swift b/CodeEdit/NavigatorSidebar/Search/Model/SearchResultModel.swift new file mode 100644 index 000000000..dd326d3c1 --- /dev/null +++ b/CodeEdit/NavigatorSidebar/Search/Model/SearchResultModel.swift @@ -0,0 +1,20 @@ +// +// SearchResultModel.swift +// CodeEdit +// +// Created by Ziyuan Zhao on 2022/3/22. +// + +import Foundation +import WorkspaceClient + +struct SearchResultModel: Hashable { + let file: WorkspaceClient.FileItem + let lineNumber: Int? + let lineContent: String? + let keywordRange: Range? + + var hasKeywordInfo: Bool { + return lineNumber != nil && lineContent != nil && keywordRange != nil + } +} diff --git a/CodeEdit/NavigatorSidebar/Search/SearchBar.swift b/CodeEdit/NavigatorSidebar/Search/SearchBar.swift index 85ce51e1a..cd0093876 100644 --- a/CodeEdit/NavigatorSidebar/Search/SearchBar.swift +++ b/CodeEdit/NavigatorSidebar/Search/SearchBar.swift @@ -8,6 +8,7 @@ import SwiftUI struct SearchBar: View { + @ObservedObject var state: WorkspaceDocument.SearchState let title: String @Binding var text: String @@ -32,6 +33,7 @@ struct SearchBar: View { private var clearButton: some View { Button { self.text = "" + state.search("") } label: { Image(systemName: "xmark.circle.fill") } @@ -43,7 +45,7 @@ struct SearchBar: View { struct SearchBar_Previews: PreviewProvider { static var previews: some View { HStack { - SearchBar(title: "placeholder", text: .constant("value")) + SearchBar(state: .init(WorkspaceDocument.init()), title: "placeholder", text: .constant("value")) } .padding() } diff --git a/CodeEdit/NavigatorSidebar/Search/SearchModeSelector.swift b/CodeEdit/NavigatorSidebar/Search/SearchModeSelector.swift index cca7dc70c..a370d28d8 100644 --- a/CodeEdit/NavigatorSidebar/Search/SearchModeSelector.swift +++ b/CodeEdit/NavigatorSidebar/Search/SearchModeSelector.swift @@ -7,75 +7,22 @@ import SwiftUI -struct SearchMode { - let title: String - let children: [SearchMode] - let needSelectionHightlight: Bool - - static let Containing = SearchMode(title: "Containing", children: [], needSelectionHightlight: false) - static let MatchingWord = SearchMode(title: "Matching Word", children: [], needSelectionHightlight: true) - static let StartingWith = SearchMode(title: "Starting With", children: [], needSelectionHightlight: true) - static let EndingWith = SearchMode(title: "Ending With", children: [], needSelectionHightlight: true) - - static let Text = SearchMode(title: "Text", children: [.Containing, .MatchingWord, .StartingWith, .EndingWith], needSelectionHightlight: false) - static let References = SearchMode(title: "References", children: [.Containing, .MatchingWord, .StartingWith, .EndingWith], needSelectionHightlight: true) - static let Definitions = SearchMode(title: "Definitions", children: [.Containing, .MatchingWord, .StartingWith, .EndingWith], needSelectionHightlight: true) - static let RegularExpression = SearchMode(title: "Regular Expression", children: [], needSelectionHightlight: true) - static let CallHierarchy = SearchMode(title: "Call Hierarchy", children: [], needSelectionHightlight: true) - - static let Find = SearchMode(title: "Find", children: [.Text, .References, .Definitions, .RegularExpression, .CallHierarchy], needSelectionHightlight: false) - static let Replace = SearchMode(title: "Replace", children: [.Text, .RegularExpression], needSelectionHightlight: true) - - static let Search = SearchMode(title: "", children: [.Find, .Replace], needSelectionHightlight: false) - - static let TextMatchingModes: [SearchMode] = [.Containing, .MatchingWord, .StartingWith, .EndingWith] - static let FindModes: [SearchMode] = [.Text, .References, .Definitions, .RegularExpression, .CallHierarchy] - static let ReplaceModes: [SearchMode] = [.Text, .RegularExpression] - static let SearchModes: [SearchMode] = [.Find, .Replace] - - static func getAllModes(_ index: Int, currentSelected: [SearchMode]) -> [SearchMode] { - switch index { - case 0: - return SearchModes - case 1: - if let searchMode = currentSelected.first { - if searchMode == SearchMode.Find { - return FindModes - } else if searchMode == searchMode { - return ReplaceModes - } - } else { - return [] - } - case 2: - return TextMatchingModes - default: - return [] - } - return [] - } -} - -extension SearchMode: Equatable { - static func ==(lhs: SearchMode, rhs: SearchMode) -> Bool { - return lhs.title == rhs.title && lhs.children == rhs.children && lhs.needSelectionHightlight == rhs.needSelectionHightlight - } -} - struct SearchModeSelector: View { - - @State var selectedMode: [SearchMode] = [ + + @State var selectedMode: [SearchModeModel] = [ .Find, .Text, .Containing ] - - private func getMenuList(_ index: Int) -> [SearchMode] { - return index == 0 ? SearchMode.SearchModes : selectedMode[index - 1].children + + private func getMenuList(_ index: Int) -> [SearchModeModel] { + return index == 0 ? SearchModeModel.SearchModes : selectedMode[index - 1].children } - - private func onSelectMenuItem(_ index: Int, searchMode: SearchMode) { - var newSelectedMode: [SearchMode] = [] + + // TODO: improve this function and remove swiftlint comment + // swiftlint:disable:next cyclomatic_complexity + private func onSelectMenuItem(_ index: Int, searchMode: SearchModeModel) { + var newSelectedMode: [SearchModeModel] = [] switch index { case 0: newSelectedMode.append(searchMode) @@ -85,7 +32,7 @@ struct SearchModeSelector: View { } else { newSelectedMode.append(secondMode) if let thirdMode = secondMode.children.first, let selectedThirdMode = selectedMode.third { - if (secondMode.children.contains(selectedThirdMode)) { + if secondMode.children.contains(selectedThirdMode) { newSelectedMode.append(selectedThirdMode) } else { newSelectedMode.append(thirdMode) @@ -107,7 +54,7 @@ struct SearchModeSelector: View { } self.selectedMode = newSelectedMode case 2: - if let firstMode = selectedMode.first, let secondMode = selectedMode.second { + if let firstMode = selectedMode.first, let secondMode = selectedMode.second { newSelectedMode.append(contentsOf: [firstMode, secondMode, searchMode]) } self.selectedMode = newSelectedMode @@ -115,18 +62,18 @@ struct SearchModeSelector: View { return } } - + private var chevron: some View { Image(systemName: "chevron.compact.right") .foregroundStyle(.secondary) .imageScale(.large) } - + var body: some View { HStack(spacing: 0) { ForEach(0.. 1 ? self[1] : nil } - + var third: Element? { return self.count > 2 ? self[2] : nil } diff --git a/CodeEdit/NavigatorSidebar/Search/SearchResultFileItem.swift b/CodeEdit/NavigatorSidebar/Search/SearchResultFileItem.swift new file mode 100644 index 000000000..e3c7db4dd --- /dev/null +++ b/CodeEdit/NavigatorSidebar/Search/SearchResultFileItem.swift @@ -0,0 +1,57 @@ +// +// SearchResultFileItem.swift +// CodeEdit +// +// Created by Ziyuan Zhao on 2022/3/22. +// + +import SwiftUI +import WorkspaceClient + +struct SearchResultFileItem: View { + @ObservedObject var state: WorkspaceDocument.SearchState + @State var isExpanded: Bool = true + var fileItem: WorkspaceClient.FileItem + var results: [SearchResultModel] + var jumpToFile: () -> Void + + private func foundLineResult(_ lineContent: String?, keywordRange: Range?) -> some View { + guard let lineContent = lineContent, let keywordRange = keywordRange else { + return AnyView(EmptyView()) + } + return AnyView( + Text(lineContent[lineContent.startIndex.. [SearchResultModel] { + return state.searchResult.filter {$0.file == file && $0.hasKeywordInfo} + } + + var body: some View { + List(selection: $selectedResult) { + ForEach(foundFiles, id: \.self) { (foundFile: SearchResultModel) in + SearchResultFileItem( + state: state, + fileItem: foundFile.file, results: getResultWith(foundFile.file)) { + state.workspace.openFile(item: foundFile.file) + } + } + }.onChange(of: selectedResult) { newValue in + if let file = newValue?.file { + state.workspace.openFile(item: file) + } + } + } +} diff --git a/CodeEdit/NavigatorSidebar/Search/SidebarFind.swift b/CodeEdit/NavigatorSidebar/Search/SidebarFind.swift new file mode 100644 index 000000000..e08b9e4c2 --- /dev/null +++ b/CodeEdit/NavigatorSidebar/Search/SidebarFind.swift @@ -0,0 +1,48 @@ +// +// SidebarSearch.swift +// CodeEdit +// +// Created by Ziyuan Zhao on 2022/3/20. +// + +import SwiftUI +import WorkspaceClient +import Combine + +struct SidebarSearch: View { + @ObservedObject var state: WorkspaceDocument.SearchState + @State private var searchText: String = "" + + private var foundFilesCount: Int { + state.searchResult.filter {!$0.hasKeywordInfo}.count + } + private var foundResultsCount: Int { + state.searchResult.filter {$0.hasKeywordInfo}.count + } + + var body: some View { + VStack { + VStack { + SearchModeSelector() + SearchBar(state: state, title: "", text: $searchText) + HStack { + Spacer() + + } + } + .padding(.horizontal, 10) + .padding(.vertical, 5) + Divider() + HStack(alignment: .center) { + Text( + "\(foundResultsCount) results in \(foundFilesCount) files") + .font(.system(size: 10)) + } + Divider() + SearchResultList(state: state) + } + .onSubmit { + state.search(searchText) + } + } +} From d994e7681b209c383b89d4e99f0c97b83cd4ce73 Mon Sep 17 00:00:00 2001 From: Ziyuan Zhao Date: Wed, 23 Mar 2022 00:35:19 +0800 Subject: [PATCH 7/8] Solve conflicts --- CodeEdit.xcodeproj/project.pbxproj | 18 ++--- .../xcshareddata/swiftpm/Package.resolved | 9 +++ .../Search/FindResultList.swift | 49 -------------- .../Search/SearchManager.swift | 67 ------------------- .../NavigatorSidebar/Search/SidebarFind.swift | 48 ------------- .../Search/SidebarSearch.swift | 16 +++-- 6 files changed, 26 insertions(+), 181 deletions(-) delete mode 100644 CodeEdit/NavigatorSidebar/Search/FindResultList.swift delete mode 100644 CodeEdit/NavigatorSidebar/Search/SearchManager.swift delete mode 100644 CodeEdit/NavigatorSidebar/Search/SidebarFind.swift diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 6d98c7284..c84487b4f 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -43,7 +43,6 @@ B6EE989027E8879A00CDD8AB /* InspectorSidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EE988F27E8879A00CDD8AB /* InspectorSidebar.swift */; }; B6EE989227E887C600CDD8AB /* InspectorSidebarToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EE989127E887C600CDD8AB /* InspectorSidebarToolbar.swift */; }; D7012EE827E757850001E1EF /* SidebarSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7012EE727E757850001E1EF /* SidebarSearch.swift */; }; - D7012EE827E757850001E1EF /* SidebarFind.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7012EE727E757850001E1EF /* SidebarFind.swift */; }; D70F5E2C27E4E8CF004EE4B9 /* WelcomeModule in Frameworks */ = {isa = PBXBuildFile; productRef = D70F5E2B27E4E8CF004EE4B9 /* WelcomeModule */; }; D7211D4327E066CE008F2ED7 /* Localized+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7211D4227E066CE008F2ED7 /* Localized+Ex.swift */; }; D7211D4727E06BFE008F2ED7 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D7211D4927E06BFE008F2ED7 /* Localizable.strings */; }; @@ -121,7 +120,6 @@ B6EE988F27E8879A00CDD8AB /* InspectorSidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorSidebar.swift; sourceTree = ""; }; B6EE989127E887C600CDD8AB /* InspectorSidebarToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorSidebarToolbar.swift; sourceTree = ""; }; D7012EE727E757850001E1EF /* SidebarSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarSearch.swift; sourceTree = ""; }; - D7012EE727E757850001E1EF /* SidebarFind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarFind.swift; sourceTree = ""; }; D7211D4227E066CE008F2ED7 /* Localized+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Localized+Ex.swift"; sourceTree = ""; }; D7211D4827E06BFE008F2ED7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; D7211D4A27E06C01008F2ED7 /* sr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sr; path = sr.lproj/Localizable.strings; sourceTree = ""; }; @@ -223,15 +221,11 @@ 287776EA27E350A100D46668 /* NavigatorSidebar */ = { isa = PBXGroup; children = ( + D7012EE627E757660001E1EF /* Search */, 287776E627E3413200D46668 /* NavigatorSidebar.swift */, 287776EC27E350D800D46668 /* NavigatorSidebarItem.swift */, 28B0A19727E385C300B73177 /* NavigatorSidebarToolbarTop.swift */, 28FFE1BE27E3A441001939DB /* NavigatorSidebarToolbarBottom.swift */, - D7012EE627E757660001E1EF /* Search */, - 287776E627E3413200D46668 /* SideBar.swift */, - 287776EC27E350D800D46668 /* SideBarItem.swift */, - 28B0A19727E385C300B73177 /* SideBarToolbarTop.swift */, - 28FFE1BE27E3A441001939DB /* SideBarToolbarBottom.swift */, ); path = NavigatorSidebar; sourceTree = ""; @@ -325,7 +319,7 @@ isa = PBXGroup; children = ( D7E201B527E9EA8A00CB86D0 /* Model */, - D7012EE727E757850001E1EF /* SidebarFind.swift */, + D7012EE727E757850001E1EF /* SidebarSearch.swift */, D7E201AF27E8C07300CB86D0 /* SearchBar.swift */, D7E201B127E8D50000CB86D0 /* SearchModeSelector.swift */, D7E201B327E9989900CB86D0 /* SearchResultList.swift */, @@ -561,7 +555,7 @@ 2875A46D27E3BE5B007805F8 /* BreadcrumbsView.swift in Sources */, 0485EB1D27E7338100138301 /* QuickOpenItem.swift in Sources */, D7E201BB27E9ECD600CB86D0 /* SearchResultModel.swift in Sources */, - D7012EE827E757850001E1EF /* SidebarFind.swift in Sources */, + D7012EE827E757850001E1EF /* SidebarSearch.swift in Sources */, D7E201AE27E8B3C000CB86D0 /* String+Ranges.swift in Sources */, 04540D5B27DD08C300E91B77 /* SettingsView.swift in Sources */, D7E201B027E8C07300CB86D0 /* SearchBar.swift in Sources */, @@ -586,11 +580,11 @@ 287776E727E3413200D46668 /* NavigatorSidebar.swift in Sources */, 287776ED27E350D800D46668 /* NavigatorSidebarItem.swift in Sources */, B6EE989027E8879A00CDD8AB /* InspectorSidebar.swift in Sources */, - D7E201B427E9989900CB86D0 /* FindResultList.swift in Sources */, + D7E201B427E9989900CB86D0 /* SearchResultList.swift in Sources */, D7E201BF27EA1ACD00CB86D0 /* FindModeModel.swift in Sources */, D7E201B427E9989900CB86D0 /* SearchResultList.swift in Sources */, - 287776E727E3413200D46668 /* SideBar.swift in Sources */, - 287776ED27E350D800D46668 /* SideBarItem.swift in Sources */, + 287776E727E3413200D46668 /* NavigatorSidebar.swift in Sources */, + 287776ED27E350D800D46668 /* NavigatorSidebarItem.swift in Sources */, 289978ED27E4E97E00BB0357 /* FileIconStyle.swift in Sources */, 04660F6627E3ACEF00477777 /* ReopenBehavior.swift in Sources */, D72E1A8327E3B0D400EB11B9 /* WelcomeView.swift in Sources */, diff --git a/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8450bfb4c..7d8fafa3f 100644 --- a/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CodeEdit.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -27,6 +27,15 @@ "revision": "f8a9c997c3c1dab4e216a8ec9014e23144cbab37", "version": "1.9.0" } + }, + { + "package": "Introspect", + "repositoryURL": "https://github.com/siteline/SwiftUI-Introspect", + "state": { + "branch": null, + "revision": "f2616860a41f9d9932da412a8978fec79c06fe24", + "version": "0.1.4" + } } ] }, diff --git a/CodeEdit/NavigatorSidebar/Search/FindResultList.swift b/CodeEdit/NavigatorSidebar/Search/FindResultList.swift deleted file mode 100644 index 2c4154304..000000000 --- a/CodeEdit/NavigatorSidebar/Search/FindResultList.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// FindResultList.swift -// CodeEdit -// -// Created by Ziyuan Zhao on 2022/3/22. -// - -import SwiftUI -import WorkspaceClient - -struct FindResultList: View { - @ObservedObject var state: WorkspaceDocument.SearchState - @State var selectedResult: AttributedString? - - var body: some View { - List(selection: $selectedResult) { - ForEach(Array(state.searchResult.keys), id: \.self) { (file: WorkspaceClient.FileItem) in - Section { - ForEach(state.searchResult[file] ?? [], id: \.self) { line in - HStack(alignment: .top) { - Image(systemName: "text.alignleft") - .font(.system(size: 12)) - .padding(.top, 2) - Text(line) - .lineLimit(Int.max) - .foregroundColor(Color(nsColor: .secondaryLabelColor)) - .font(.system(size: 12, weight: .light)) - } - .padding(.leading, 15) - .tag(line) - .onTapGesture { - state.workspace.openFile(item: file) - } - } - } header: { - HStack(alignment: .center) { -// Image(nsImage: NSWorkspace.shared.icon(forFile: fileURL.path)) -// .frame(width: 13, height: 13) - Text(file.url.lastPathComponent) - .font(.system(size: 13, weight: .semibold)) - .foregroundColor(Color(nsColor: NSColor.headerTextColor)) - Text(file.url.path.replacingOccurrences(of: state.workspace.fileURL?.path ?? "", with: "")) - } - .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) - } - } - } - } -} diff --git a/CodeEdit/NavigatorSidebar/Search/SearchManager.swift b/CodeEdit/NavigatorSidebar/Search/SearchManager.swift deleted file mode 100644 index 399524fe7..000000000 --- a/CodeEdit/NavigatorSidebar/Search/SearchManager.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// SearchManager.swift -// CodeEdit -// -// Created by Ziyuan Zhao on 2022/3/20. -// - -import Foundation -import WorkspaceClient -import Combine - -class SearchManager: ObservableObject { - @Published var searchResult: [WorkspaceClient.FileItem: [AttributedString]] = [:] - private var cancellables = Set() - - func search(_ text: String, workspaceClient: WorkspaceClient?) { - searchResult = [:] - workspaceClient? - .getFiles - .sink { [weak self] files in - guard let self = self else { return } - files.forEach { fileItem in - let data = try? String(contentsOf: fileItem.url) - data?.split(separator: "\n").forEach { line in - if line.contains(text) { - line.ranges(of: text).forEach { range in - var attributedString = AttributedString() - attributedString.append( - AttributedString(String(line[line.startIndex..( - of substring: T, - options: String.CompareOptions = [], - locale: Locale? = nil - ) -> [Range] { - var ranges: [Range] = [] - while let result = range( - of: substring, - options: options, - range: (ranges.last?.upperBound ?? startIndex).. Date: Wed, 23 Mar 2022 01:28:46 +0800 Subject: [PATCH 8/8] Move models to package --- CodeEdit.xcodeproj/project.pbxproj | 23 ++--- CodeEdit/Documents/WorkspaceDocument.swift | 2 +- .../Search/Model/FindModeModel.swift | 75 ---------------- .../Search/Model/SearchResultModel.swift | 20 ----- .../Search/SearchModeSelector.swift | 2 +- .../Search/SearchResultFileItem.swift | 9 +- .../Search/SearchResultList.swift | 1 + .../Search/SidebarSearch.swift | 4 +- .../Search/src/Model/SearchModeModel.swift | 85 +++++++++++++++++++ .../Search/src/Model/SearchResultModel.swift | 32 +++++++ CodeEditModules/Package.swift | 12 ++- 11 files changed, 146 insertions(+), 119 deletions(-) delete mode 100644 CodeEdit/NavigatorSidebar/Search/Model/FindModeModel.swift delete mode 100644 CodeEdit/NavigatorSidebar/Search/Model/SearchResultModel.swift create mode 100644 CodeEditModules/Modules/Search/src/Model/SearchModeModel.swift create mode 100644 CodeEditModules/Modules/Search/src/Model/SearchResultModel.swift diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index c84487b4f..d2475b93c 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -53,9 +53,8 @@ D7E201B027E8C07300CB86D0 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201AF27E8C07300CB86D0 /* SearchBar.swift */; }; D7E201B227E8D50000CB86D0 /* SearchModeSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201B127E8D50000CB86D0 /* SearchModeSelector.swift */; }; D7E201B427E9989900CB86D0 /* SearchResultList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201B327E9989900CB86D0 /* SearchResultList.swift */; }; - D7E201BB27E9ECD600CB86D0 /* SearchResultModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201BA27E9ECD600CB86D0 /* SearchResultModel.swift */; }; D7E201BD27EA00E200CB86D0 /* SearchResultFileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201BC27EA00E200CB86D0 /* SearchResultFileItem.swift */; }; - D7E201BF27EA1ACD00CB86D0 /* FindModeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201BE27EA1ACD00CB86D0 /* FindModeModel.swift */; }; + D7F72DEB27EA3574000C3064 /* Search in Frameworks */ = {isa = PBXBuildFile; productRef = D7F72DEA27EA3574000C3064 /* Search */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -130,9 +129,7 @@ D7E201AF27E8C07300CB86D0 /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = ""; }; D7E201B127E8D50000CB86D0 /* SearchModeSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModeSelector.swift; sourceTree = ""; }; D7E201B327E9989900CB86D0 /* SearchResultList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultList.swift; sourceTree = ""; }; - D7E201BA27E9ECD600CB86D0 /* SearchResultModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultModel.swift; sourceTree = ""; }; D7E201BC27EA00E200CB86D0 /* SearchResultFileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultFileItem.swift; sourceTree = ""; }; - D7E201BE27EA1ACD00CB86D0 /* FindModeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindModeModel.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -144,6 +141,7 @@ 0485EB2527E7B9C800138301 /* Overlays in Frameworks */, B65E614627E6765D00255275 /* Introspect in Frameworks */, D70F5E2C27E4E8CF004EE4B9 /* WelcomeModule in Frameworks */, + D7F72DEB27EA3574000C3064 /* Search in Frameworks */, 5CFA753B27E896B60002F01B /* GitClient in Frameworks */, 28CE5EA027E6493D0065D29C /* StatusBar in Frameworks */, 5CF38A5E27E48E6C0096A0F7 /* CodeFile in Frameworks */, @@ -318,7 +316,6 @@ D7012EE627E757660001E1EF /* Search */ = { isa = PBXGroup; children = ( - D7E201B527E9EA8A00CB86D0 /* Model */, D7012EE727E757850001E1EF /* SidebarSearch.swift */, D7E201AF27E8C07300CB86D0 /* SearchBar.swift */, D7E201B127E8D50000CB86D0 /* SearchModeSelector.swift */, @@ -347,15 +344,6 @@ path = Welcome; sourceTree = ""; }; - D7E201B527E9EA8A00CB86D0 /* Model */ = { - isa = PBXGroup; - children = ( - D7E201BA27E9ECD600CB86D0 /* SearchResultModel.swift */, - D7E201BE27EA1ACD00CB86D0 /* FindModeModel.swift */, - ); - path = Model; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -382,6 +370,7 @@ 28CE5E9F27E6493D0065D29C /* StatusBar */, 0485EB2427E7B9C800138301 /* Overlays */, 5CFA753A27E896B60002F01B /* GitClient */, + D7F72DEA27EA3574000C3064 /* Search */, ); productName = CodeEdit; productReference = B658FB2C27DA9E0F00EA4DBD /* CodeEdit.app */; @@ -554,7 +543,6 @@ 2B7A583527E4BA0100D25D4E /* AppDelegate.swift in Sources */, 2875A46D27E3BE5B007805F8 /* BreadcrumbsView.swift in Sources */, 0485EB1D27E7338100138301 /* QuickOpenItem.swift in Sources */, - D7E201BB27E9ECD600CB86D0 /* SearchResultModel.swift in Sources */, D7012EE827E757850001E1EF /* SidebarSearch.swift in Sources */, D7E201AE27E8B3C000CB86D0 /* String+Ranges.swift in Sources */, 04540D5B27DD08C300E91B77 /* SettingsView.swift in Sources */, @@ -581,7 +569,6 @@ 287776ED27E350D800D46668 /* NavigatorSidebarItem.swift in Sources */, B6EE989027E8879A00CDD8AB /* InspectorSidebar.swift in Sources */, D7E201B427E9989900CB86D0 /* SearchResultList.swift in Sources */, - D7E201BF27EA1ACD00CB86D0 /* FindModeModel.swift in Sources */, D7E201B427E9989900CB86D0 /* SearchResultList.swift in Sources */, 287776E727E3413200D46668 /* NavigatorSidebar.swift in Sources */, 287776ED27E350D800D46668 /* NavigatorSidebarItem.swift in Sources */, @@ -1003,6 +990,10 @@ isa = XCSwiftPackageProductDependency; productName = WelcomeModule; }; + D7F72DEA27EA3574000C3064 /* Search */ = { + isa = XCSwiftPackageProductDependency; + productName = Search; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = B658FB2427DA9E0F00EA4DBD /* Project object */; diff --git a/CodeEdit/Documents/WorkspaceDocument.swift b/CodeEdit/Documents/WorkspaceDocument.swift index d90182776..cb55930b7 100644 --- a/CodeEdit/Documents/WorkspaceDocument.swift +++ b/CodeEdit/Documents/WorkspaceDocument.swift @@ -11,6 +11,7 @@ import SwiftUI import WorkspaceClient import Combine import CodeFile +import Search @objc(WorkspaceDocument) class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate { @@ -179,7 +180,6 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate { extension WorkspaceDocument { class SearchState: ObservableObject { - var workspace: WorkspaceDocument @Published var searchResult: [SearchResultModel] = [] diff --git a/CodeEdit/NavigatorSidebar/Search/Model/FindModeModel.swift b/CodeEdit/NavigatorSidebar/Search/Model/FindModeModel.swift deleted file mode 100644 index 986972c74..000000000 --- a/CodeEdit/NavigatorSidebar/Search/Model/FindModeModel.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// SearchModeModel.swift -// CodeEdit -// -// Created by Ziyuan Zhao on 2022/3/22. -// - -import Foundation - -struct SearchModeModel { - let title: String - let children: [SearchModeModel] - let needSelectionHightlight: Bool - - static let Containing = SearchModeModel(title: "Containing", children: [], needSelectionHightlight: false) - static let MatchingWord = SearchModeModel(title: "Matching Word", children: [], needSelectionHightlight: true) - static let StartingWith = SearchModeModel(title: "Starting With", children: [], needSelectionHightlight: true) - static let EndingWith = SearchModeModel(title: "Ending With", children: [], needSelectionHightlight: true) - - static let Text = SearchModeModel(title: "Text", - children: [.Containing, .MatchingWord, .StartingWith, .EndingWith], - needSelectionHightlight: false) - static let References = SearchModeModel(title: "References", - children: [.Containing, .MatchingWord, .StartingWith, .EndingWith], - needSelectionHightlight: true) - static let Definitions = SearchModeModel(title: "Definitions", - children: [.Containing, .MatchingWord, .StartingWith, .EndingWith], - needSelectionHightlight: true) - static let RegularExpression = SearchModeModel(title: "Regular Expression", - children: [], - needSelectionHightlight: true) - static let CallHierarchy = SearchModeModel(title: "Call Hierarchy", children: [], needSelectionHightlight: true) - - static let Find = SearchModeModel(title: "Find", - children: [.Text, .References, .Definitions, .RegularExpression, .CallHierarchy], - needSelectionHightlight: false) - static let Replace = SearchModeModel(title: "Replace", - children: [.Text, .RegularExpression], - needSelectionHightlight: true) - - static let TextMatchingModes: [SearchModeModel] = [.Containing, .MatchingWord, .StartingWith, .EndingWith] - static let FindModes: [SearchModeModel] = [.Text, .References, .Definitions, .RegularExpression, .CallHierarchy] - static let ReplaceModes: [SearchModeModel] = [.Text, .RegularExpression] - static let SearchModes: [SearchModeModel] = [.Find, .Replace] - - static func getAllModes(_ index: Int, currentSelected: [SearchModeModel]) -> [SearchModeModel] { - switch index { - case 0: - return SearchModes - case 1: - if let searchMode = currentSelected.first { - if searchMode == SearchModeModel.Find { - return SearchModes - } else if searchMode == searchMode { - return ReplaceModes - } - } else { - return [] - } - case 2: - return TextMatchingModes - default: - return [] - } - return [] - } -} - -extension SearchModeModel: Equatable { - static func == (lhs: SearchModeModel, rhs: SearchModeModel) -> Bool { - return lhs.title == rhs.title - && lhs.children == rhs.children - && lhs.needSelectionHightlight == rhs.needSelectionHightlight - } -} diff --git a/CodeEdit/NavigatorSidebar/Search/Model/SearchResultModel.swift b/CodeEdit/NavigatorSidebar/Search/Model/SearchResultModel.swift deleted file mode 100644 index dd326d3c1..000000000 --- a/CodeEdit/NavigatorSidebar/Search/Model/SearchResultModel.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// SearchResultModel.swift -// CodeEdit -// -// Created by Ziyuan Zhao on 2022/3/22. -// - -import Foundation -import WorkspaceClient - -struct SearchResultModel: Hashable { - let file: WorkspaceClient.FileItem - let lineNumber: Int? - let lineContent: String? - let keywordRange: Range? - - var hasKeywordInfo: Bool { - return lineNumber != nil && lineContent != nil && keywordRange != nil - } -} diff --git a/CodeEdit/NavigatorSidebar/Search/SearchModeSelector.swift b/CodeEdit/NavigatorSidebar/Search/SearchModeSelector.swift index a370d28d8..f9654f3b3 100644 --- a/CodeEdit/NavigatorSidebar/Search/SearchModeSelector.swift +++ b/CodeEdit/NavigatorSidebar/Search/SearchModeSelector.swift @@ -6,9 +6,9 @@ // import SwiftUI +import Search struct SearchModeSelector: View { - @State var selectedMode: [SearchModeModel] = [ .Find, .Text, diff --git a/CodeEdit/NavigatorSidebar/Search/SearchResultFileItem.swift b/CodeEdit/NavigatorSidebar/Search/SearchResultFileItem.swift index e3c7db4dd..8a58cb651 100644 --- a/CodeEdit/NavigatorSidebar/Search/SearchResultFileItem.swift +++ b/CodeEdit/NavigatorSidebar/Search/SearchResultFileItem.swift @@ -7,6 +7,7 @@ import SwiftUI import WorkspaceClient +import Search struct SearchResultFileItem: View { @ObservedObject var state: WorkspaceDocument.SearchState @@ -31,7 +32,7 @@ struct SearchResultFileItem: View { var body: some View { DisclosureGroup(isExpanded: $isExpanded) { ForEach(results, id: \.lineContent) { (result: SearchResultModel) in - HStack(alignment: .top) { + HStack(alignment: .top, spacing: 4) { Image(systemName: "text.alignleft") .font(.system(size: 12)) .padding(.top, 2) @@ -41,15 +42,17 @@ struct SearchResultFileItem: View { .font(.system(size: 12, weight: .light)) Spacer() } - .padding(.leading, 15) } } label: { HStack { Image(systemName: fileItem.fileIcon) Text(fileItem.fileName) - .font(.system(size: 13, weight: .semibold)) .foregroundColor(Color(nsColor: NSColor.headerTextColor)) + .font(.system(size: 13, weight: .semibold)) + + Text(" ") + Text(fileItem.url.path.replacingOccurrences(of: state.workspace.fileURL?.path ?? "", with: "")) + .foregroundColor(.secondary) + .font(.system(size: 12, weight: .light)) Spacer() } } diff --git a/CodeEdit/NavigatorSidebar/Search/SearchResultList.swift b/CodeEdit/NavigatorSidebar/Search/SearchResultList.swift index 4a071bd01..a64a00e7c 100644 --- a/CodeEdit/NavigatorSidebar/Search/SearchResultList.swift +++ b/CodeEdit/NavigatorSidebar/Search/SearchResultList.swift @@ -7,6 +7,7 @@ import SwiftUI import WorkspaceClient +import Search struct SearchResultList: View { @ObservedObject var state: WorkspaceDocument.SearchState diff --git a/CodeEdit/NavigatorSidebar/Search/SidebarSearch.swift b/CodeEdit/NavigatorSidebar/Search/SidebarSearch.swift index e08b9e4c2..7276a0951 100644 --- a/CodeEdit/NavigatorSidebar/Search/SidebarSearch.swift +++ b/CodeEdit/NavigatorSidebar/Search/SidebarSearch.swift @@ -7,7 +7,7 @@ import SwiftUI import WorkspaceClient -import Combine +import Search struct SidebarSearch: View { @ObservedObject var state: WorkspaceDocument.SearchState @@ -16,6 +16,7 @@ struct SidebarSearch: View { private var foundFilesCount: Int { state.searchResult.filter {!$0.hasKeywordInfo}.count } + private var foundResultsCount: Int { state.searchResult.filter {$0.hasKeywordInfo}.count } @@ -27,7 +28,6 @@ struct SidebarSearch: View { SearchBar(state: state, title: "", text: $searchText) HStack { Spacer() - } } .padding(.horizontal, 10) diff --git a/CodeEditModules/Modules/Search/src/Model/SearchModeModel.swift b/CodeEditModules/Modules/Search/src/Model/SearchModeModel.swift new file mode 100644 index 000000000..9c237ea96 --- /dev/null +++ b/CodeEditModules/Modules/Search/src/Model/SearchModeModel.swift @@ -0,0 +1,85 @@ +// +// SearchModeModel.swift +// CodeEdit +// +// Created by Ziyuan Zhao on 2022/3/22. +// + +import Foundation + +public struct SearchModeModel { + public let title: String + public let children: [SearchModeModel] + public let needSelectionHightlight: Bool + + public static let Containing = SearchModeModel(title: "Containing", children: [], needSelectionHightlight: false) + public static let MatchingWord = SearchModeModel(title: "Matching Word", + children: [], + needSelectionHightlight: true) + public static let StartingWith = SearchModeModel(title: "Starting With", + children: [], + needSelectionHightlight: true) + public static let EndingWith = SearchModeModel(title: "Ending With", children: [], needSelectionHightlight: true) + + public static let Text = SearchModeModel(title: "Text", + children: [.Containing, .MatchingWord, .StartingWith, .EndingWith], + needSelectionHightlight: false) + public static let References = SearchModeModel(title: "References", + children: [.Containing, .MatchingWord, .StartingWith, .EndingWith], + needSelectionHightlight: true) + public static let Definitions = SearchModeModel(title: "Definitions", + children: [.Containing, .MatchingWord, .StartingWith, .EndingWith], + needSelectionHightlight: true) + public static let RegularExpression = SearchModeModel(title: "Regular Expression", + children: [], + needSelectionHightlight: true) + public static let CallHierarchy = SearchModeModel(title: "Call Hierarchy", + children: [], + needSelectionHightlight: true) + + public static let Find = SearchModeModel(title: "Find", + children: [.Text, .References, .Definitions, .RegularExpression, .CallHierarchy], + needSelectionHightlight: false) + public static let Replace = SearchModeModel(title: "Replace", + children: [.Text, .RegularExpression], + needSelectionHightlight: true) + + public static let TextMatchingModes: [SearchModeModel] = [.Containing, .MatchingWord, .StartingWith, .EndingWith] + public static let FindModes: [SearchModeModel] = [.Text, + .References, + .Definitions, + .RegularExpression, + .CallHierarchy] + public static let ReplaceModes: [SearchModeModel] = [.Text, .RegularExpression] + public static let SearchModes: [SearchModeModel] = [.Find, .Replace] + + public static func getAllModes(_ index: Int, currentSelected: [SearchModeModel]) -> [SearchModeModel] { + switch index { + case 0: + return SearchModes + case 1: + if let searchMode = currentSelected.first { + if searchMode == SearchModeModel.Find { + return SearchModes + } else if searchMode == searchMode { + return ReplaceModes + } + } else { + return [] + } + case 2: + return TextMatchingModes + default: + return [] + } + return [] + } +} + +extension SearchModeModel: Equatable { + public static func == (lhs: SearchModeModel, rhs: SearchModeModel) -> Bool { + return lhs.title == rhs.title + && lhs.children == rhs.children + && lhs.needSelectionHightlight == rhs.needSelectionHightlight + } +} diff --git a/CodeEditModules/Modules/Search/src/Model/SearchResultModel.swift b/CodeEditModules/Modules/Search/src/Model/SearchResultModel.swift new file mode 100644 index 000000000..1a64b572f --- /dev/null +++ b/CodeEditModules/Modules/Search/src/Model/SearchResultModel.swift @@ -0,0 +1,32 @@ +// +// SearchResultModel.swift +// CodeEdit +// +// Created by Ziyuan Zhao on 2022/3/22. +// + +import Foundation +import WorkspaceClient + +public struct SearchResultModel: Hashable { + public var file: WorkspaceClient.FileItem + public var lineNumber: Int? + public var lineContent: String? + public var keywordRange: Range? + + public init( + file: WorkspaceClient.FileItem, + lineNumber: Int?, + lineContent: String?, + keywordRange: Range? + ) { + self.file = file + self.lineNumber = lineNumber + self.lineContent = lineContent + self.keywordRange = keywordRange + } + + public var hasKeywordInfo: Bool { + return lineNumber != nil && lineContent != nil && keywordRange != nil + } +} diff --git a/CodeEditModules/Package.swift b/CodeEditModules/Package.swift index 91d100d51..6e86e48f3 100644 --- a/CodeEditModules/Package.swift +++ b/CodeEditModules/Package.swift @@ -32,6 +32,10 @@ let package = Package( .library( name: "GitClient", targets: ["GitClient"] + ), + .library( + name: "Search", + targets: ["Search"] ) ], dependencies: [ @@ -101,7 +105,13 @@ let package = Package( .target( name: "GitClient", path: "Modules/GitClient/src" + ), + .target( + name: "Search", + dependencies: [ + "WorkspaceClient" + ], + path: "Modules/Search/src" ) - ] )