Skip to content

Finding in workspace #125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions CodeEdit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,19 @@
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 */; };
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 */; };
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 */; };
D7E201BD27EA00E200CB86D0 /* SearchResultFileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201BC27EA00E200CB86D0 /* SearchResultFileItem.swift */; };
D7F72DEB27EA3574000C3064 /* Search in Frameworks */ = {isa = PBXBuildFile; productRef = D7F72DEA27EA3574000C3064 /* Search */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -111,12 +118,18 @@
B673FDAC27E8296A00795864 /* PressActionsModifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PressActionsModifier.swift; sourceTree = "<group>"; };
B6EE988F27E8879A00CDD8AB /* InspectorSidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorSidebar.swift; sourceTree = "<group>"; };
B6EE989127E887C600CDD8AB /* InspectorSidebarToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorSidebarToolbar.swift; sourceTree = "<group>"; };
D7012EE727E757850001E1EF /* SidebarSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarSearch.swift; sourceTree = "<group>"; };
D7211D4227E066CE008F2ED7 /* Localized+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Localized+Ex.swift"; sourceTree = "<group>"; };
D7211D4827E06BFE008F2ED7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
D7211D4A27E06C01008F2ED7 /* sr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sr; path = sr.lproj/Localizable.strings; sourceTree = "<group>"; };
D72E1A8227E3B0D400EB11B9 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
D72E1A8627E4242900EB11B9 /* RecentProjectsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentProjectsView.swift; sourceTree = "<group>"; };
D72E1A8827E44D7C00EB11B9 /* WelcomeWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeWindowView.swift; sourceTree = "<group>"; };
D7E201AD27E8B3C000CB86D0 /* String+Ranges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Ranges.swift"; sourceTree = "<group>"; };
D7E201AF27E8C07300CB86D0 /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = "<group>"; };
D7E201B127E8D50000CB86D0 /* SearchModeSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModeSelector.swift; sourceTree = "<group>"; };
D7E201B327E9989900CB86D0 /* SearchResultList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultList.swift; sourceTree = "<group>"; };
D7E201BC27EA00E200CB86D0 /* SearchResultFileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultFileItem.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -128,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 */,
Expand Down Expand Up @@ -158,6 +172,7 @@
043C321527E3201F006AE443 /* WorkspaceDocument.swift */,
04660F6927E51E5C00477777 /* CodeEditWindowController.swift */,
0485EB1E27E7458B00138301 /* WorkspaceCodeFileView.swift */,
D7E201AD27E8B3C000CB86D0 /* String+Ranges.swift */,
);
path = Documents;
sourceTree = "<group>";
Expand Down Expand Up @@ -204,6 +219,7 @@
287776EA27E350A100D46668 /* NavigatorSidebar */ = {
isa = PBXGroup;
children = (
D7012EE627E757660001E1EF /* Search */,
287776E627E3413200D46668 /* NavigatorSidebar.swift */,
287776EC27E350D800D46668 /* NavigatorSidebarItem.swift */,
28B0A19727E385C300B73177 /* NavigatorSidebarToolbarTop.swift */,
Expand Down Expand Up @@ -297,6 +313,18 @@
path = InspectorSidebar;
sourceTree = "<group>";
};
D7012EE627E757660001E1EF /* Search */ = {
isa = PBXGroup;
children = (
D7012EE727E757850001E1EF /* SidebarSearch.swift */,
D7E201AF27E8C07300CB86D0 /* SearchBar.swift */,
D7E201B127E8D50000CB86D0 /* SearchModeSelector.swift */,
D7E201B327E9989900CB86D0 /* SearchResultList.swift */,
D7E201BC27EA00E200CB86D0 /* SearchResultFileItem.swift */,
);
path = Search;
sourceTree = "<group>";
};
D7211D4427E066D4008F2ED7 /* Localization */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -342,6 +370,7 @@
28CE5E9F27E6493D0065D29C /* StatusBar */,
0485EB2427E7B9C800138301 /* Overlays */,
5CFA753A27E896B60002F01B /* GitClient */,
D7F72DEA27EA3574000C3064 /* Search */,
);
productName = CodeEdit;
productReference = B658FB2C27DA9E0F00EA4DBD /* CodeEdit.app */;
Expand Down Expand Up @@ -514,7 +543,10 @@
2B7A583527E4BA0100D25D4E /* AppDelegate.swift in Sources */,
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 */,
D7E201B027E8C07300CB86D0 /* SearchBar.swift in Sources */,
D72E1A8927E44D7C00EB11B9 /* WelcomeWindowView.swift in Sources */,
04540D5C27DD08C300E91B77 /* GeneralSettingsView.swift in Sources */,
B6EE989227E887C600CDD8AB /* InspectorSidebarToolbar.swift in Sources */,
Expand All @@ -524,16 +556,22 @@
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 */,
D7E201BD27EA00E200CB86D0 /* SearchResultFileItem.swift in Sources */,
286620A527E4AB6900E18C2B /* BreadcrumbsComponent.swift in Sources */,
287776EF27E3515300D46668 /* TabBarItem.swift in Sources */,
D72E1A8727E4242900EB11B9 /* RecentProjectsView.swift in Sources */,
04660F6A27E51E5C00477777 /* CodeEditWindowController.swift in Sources */,
287776E727E3413200D46668 /* NavigatorSidebar.swift in Sources */,
287776ED27E350D800D46668 /* NavigatorSidebarItem.swift in Sources */,
B6EE989027E8879A00CDD8AB /* InspectorSidebar.swift in Sources */,
D7E201B427E9989900CB86D0 /* SearchResultList.swift in Sources */,
D7E201B427E9989900CB86D0 /* SearchResultList.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 */,
Expand Down Expand Up @@ -952,6 +990,10 @@
isa = XCSwiftPackageProductDependency;
productName = WelcomeModule;
};
D7F72DEA27EA3574000C3064 /* Search */ = {
isa = XCSwiftPackageProductDependency;
productName = Search;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = B658FB2427DA9E0F00EA4DBD /* Project object */;
Expand Down
26 changes: 26 additions & 0 deletions CodeEdit/Documents/String+Ranges.swift
Original file line number Diff line number Diff line change
@@ -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<T: StringProtocol>(
of substring: T,
options: String.CompareOptions = [],
locale: Locale? = nil
) -> [Range<Index>] {
var ranges: [Range<Index>] = []
while let result = range(
of: substring,
options: options,
range: (ranges.last?.upperBound ?? startIndex)..<endIndex,
locale: locale) {
ranges.append(result)
}
return ranges
}
}
65 changes: 65 additions & 0 deletions CodeEdit/Documents/WorkspaceDocument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import SwiftUI
import WorkspaceClient
import Combine
import CodeFile
import Search

@objc(WorkspaceDocument)
class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
Expand All @@ -26,6 +27,7 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
guard let selectedId = selectedId else { return nil }
return fileItems.first(where: { $0.id == selectedId })
}
var searchState: SearchState?
var quickOpenState: QuickOpenState?
var openedCodeFiles: [WorkspaceClient.FileItem: CodeFileDocument] = [:]
private var cancellables = Set<AnyCancellable>()
Expand Down Expand Up @@ -132,6 +134,7 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
ignoredFilesAndFolders: ignoredFilesAndDirectory
)
directoryURL = url
self.searchState = .init(self)
self.quickOpenState = .init(self)
workspaceClient?
.getFiles
Expand Down Expand Up @@ -173,6 +176,68 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
}
}

// MARK: - Search

extension WorkspaceDocument {
class SearchState: ObservableObject {
var workspace: WorkspaceDocument
@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: [
.isRegularFileKey
],
options: [
.skipsHiddenFiles,
.skipsPackageDescendants
])
if let filePaths = enumerator?.allObjects as? [URL] {
filePaths.map { url in
WorkspaceClient.FileItem(url: url, children: nil)
}.forEach { fileItem in
var fileAddedFlag = true
do {
let data = try Data(contentsOf: fileItem.url)
data.withUnsafeBytes {
$0.split(separator: UInt8(ascii: "\n"))
.map { String(decoding: UnsafeRawBufferPointer(rebasing: $0), as: UTF8.self) }
}.enumerated().forEach { (index: Int, line: String) in
let noSpaceLine = line.trimmingCharacters(in: .whitespaces)
if noSpaceLine.contains(text) {
if fileAddedFlag {
searchResult.append(SearchResultModel(
file: fileItem,
lineNumber: nil,
lineContent: nil,
keywordRange: nil)
)
fileAddedFlag = false
}
noSpaceLine.ranges(of: text).forEach { range in
searchResult.append(SearchResultModel(
file: fileItem,
lineNumber: index,
lineContent: noSpaceLine,
keywordRange: range)
)
}
}
}
} catch {}
}
}
}
}
}
}

// MARK: - Quick Open

extension WorkspaceDocument {
Expand Down
24 changes: 14 additions & 10 deletions CodeEdit/NavigatorSidebar/NavigatorSidebar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(state: workspace.searchState ?? .init(workspace))
default: EmptyView()
}
}
Expand Down
52 changes: 52 additions & 0 deletions CodeEdit/NavigatorSidebar/Search/SearchBar.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// SearchBar.swift
// CodeEdit
//
// Created by Ziyuan Zhao on 2022/3/21.
//

import SwiftUI

struct SearchBar: View {
@ObservedObject var state: WorkspaceDocument.SearchState
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 = ""
state.search("")
} label: {
Image(systemName: "xmark.circle.fill")
}
.foregroundColor(.secondary)
.buttonStyle(PlainButtonStyle())
}
}

struct SearchBar_Previews: PreviewProvider {
static var previews: some View {
HStack {
SearchBar(state: .init(WorkspaceDocument.init()), title: "placeholder", text: .constant("value"))
}
.padding()
}
}
Loading