mirror of
https://github.com/coteditor/CotEditor.git
synced 2024-09-11 11:25:57 +03:00
Display concrete find progress
This commit is contained in:
parent
865004349d
commit
95f8734584
@ -7,6 +7,8 @@ Change Log
|
||||
|
||||
### Improvements
|
||||
|
||||
- Optimize large find/replace task performance.
|
||||
- Display the concrete progress of the find/replace task in the progress dialog.
|
||||
- Update the Unicode block name list for the character inspector from Unicode 14.0.0 to Unicode 15.0.0.
|
||||
- [trivial] Tweak the setting summary display in the print dialog.
|
||||
|
||||
|
@ -29,14 +29,32 @@ final class FindProgress: ObservableObject {
|
||||
|
||||
var count = 0
|
||||
|
||||
var scope: Range<Int>
|
||||
var completedUnit = 0
|
||||
|
||||
@Published var isCancelled = false
|
||||
@Published var isFinished = false
|
||||
|
||||
|
||||
/// The fraction of task completed in between 0...1.0.
|
||||
var fractionCompleted: Double? {
|
||||
/// Instantiate a progress.
|
||||
///
|
||||
/// - Parameter scope: The range of progress unit to work with.
|
||||
init(scope: Range<Int>) {
|
||||
|
||||
self.isFinished ? 1 : nil
|
||||
self.scope = scope
|
||||
}
|
||||
|
||||
|
||||
/// The fraction of task completed in between 0...1.0.
|
||||
var fractionCompleted: Double {
|
||||
|
||||
if self.isFinished {
|
||||
return 1
|
||||
} else if self.scope.isEmpty {
|
||||
return 0
|
||||
} else {
|
||||
return Double(self.completedUnit) / Double(self.scope.count)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ struct FindProgressView_Previews: PreviewProvider {
|
||||
|
||||
static var previews: some View {
|
||||
|
||||
FindProgressView("Label", unit: .find, progress: .init())
|
||||
FindProgressView("Label", unit: .find, progress: .init(scope: 0..<100))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ extension MultipleReplacement {
|
||||
textView.isEditable = false
|
||||
|
||||
// setup progress sheet
|
||||
let progress = FindProgress()
|
||||
let progress = FindProgress(scope: 0..<self.replacements.count)
|
||||
let closesAutomatically = UserDefaults.standard[.findClosesIndicatorWhenDone]
|
||||
let indicatorView = FindProgressView("Highlight All", unit: .find, progress: progress)
|
||||
let indicator = NSHostingController(rootView: indicatorView)
|
||||
@ -56,8 +56,10 @@ extension MultipleReplacement {
|
||||
stop = true
|
||||
return
|
||||
}
|
||||
|
||||
progress.count += 1
|
||||
|
||||
} unitChanged: {
|
||||
progress.completedUnit += 1
|
||||
}
|
||||
.sorted(\.location)
|
||||
|
||||
@ -110,7 +112,7 @@ extension MultipleReplacement {
|
||||
textView.isEditable = false
|
||||
|
||||
// setup progress sheet
|
||||
let progress = FindProgress()
|
||||
let progress = FindProgress(scope: 0..<self.replacements.count)
|
||||
let closesAutomatically = UserDefaults.standard[.findClosesIndicatorWhenDone]
|
||||
let indicatorView = FindProgressView("Replace All", unit: .replacement, progress: progress)
|
||||
let indicator = NSHostingController(rootView: indicatorView)
|
||||
@ -125,8 +127,10 @@ extension MultipleReplacement {
|
||||
stop = true
|
||||
return
|
||||
}
|
||||
|
||||
progress.count += 1
|
||||
|
||||
} unitChanged: {
|
||||
progress.completedUnit += 1
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
@ -87,8 +87,9 @@ extension MultipleReplacement {
|
||||
/// - inSelection: Whether find only in selection.
|
||||
/// - block: The block enumerates the matches.
|
||||
/// - stop: A reference to a Bool value. The block can set the value to true to stop further processing.
|
||||
/// - unitChanged: The block invoked when the task did change to the next replecement definition.
|
||||
/// - Returns: The found ranges. This method will return first all search finished.
|
||||
func find(string: String, ranges: [NSRange], inSelection: Bool, using block: (_ stop: inout Bool) -> Void) -> [NSRange] {
|
||||
func find(string: String, ranges: [NSRange], inSelection: Bool, using block: (_ stop: inout Bool) -> Void, unitChanged: () -> Void) -> [NSRange] {
|
||||
|
||||
var result: [NSRange] = []
|
||||
|
||||
@ -110,6 +111,9 @@ extension MultipleReplacement {
|
||||
}
|
||||
|
||||
guard !isCancelled else { return [] }
|
||||
|
||||
// notify
|
||||
unitChanged()
|
||||
}
|
||||
|
||||
return result
|
||||
@ -124,8 +128,9 @@ extension MultipleReplacement {
|
||||
/// - inSelection: Whether replace only in selection.
|
||||
/// - block: The block enumerates the matches.
|
||||
/// - stop: A reference to a Bool value. The block can set the value to true to stop further processing.
|
||||
/// - unitChanged: The block invoked when the task did change to the next replecement definition.
|
||||
/// - Returns: The result of the replacement. This method will return first all replacement finished.
|
||||
func replace(string: String, ranges: [NSRange], inSelection: Bool, using block: @escaping (_ stop: inout Bool) -> Void) -> Result {
|
||||
func replace(string: String, ranges: [NSRange], inSelection: Bool, using block: @escaping (_ stop: inout Bool) -> Void, unitChanged: () -> Void) -> Result {
|
||||
|
||||
var result = Result(string: string, selectedRanges: ranges)
|
||||
|
||||
@ -140,7 +145,7 @@ extension MultipleReplacement {
|
||||
|
||||
// process replacement
|
||||
var isCancelled = false
|
||||
let (replacementItems, selectedRanges) = textFind.replaceAll(with: replacement.replacementString) { (flag, stop) in
|
||||
let (replacementItems, selectedRanges) = textFind.replaceAll(with: replacement.replacementString) { (flag, _, stop) in
|
||||
|
||||
switch flag {
|
||||
case .findProgress:
|
||||
@ -162,6 +167,9 @@ extension MultipleReplacement {
|
||||
|
||||
// update selected ranges
|
||||
result.selectedRanges = selectedRanges
|
||||
|
||||
// notify
|
||||
unitChanged()
|
||||
}
|
||||
|
||||
return result
|
||||
|
@ -172,6 +172,13 @@ final class TextFind {
|
||||
}
|
||||
|
||||
|
||||
/// The range large enough to contain all scope ranges.
|
||||
var scopeRange: Range<Int> {
|
||||
|
||||
self.scopeRanges.map(\.lowerBound).min()!..<self.scopeRanges.map(\.upperBound).max()!
|
||||
}
|
||||
|
||||
|
||||
/// Return the nearest match from the insertion point.
|
||||
///
|
||||
/// - Parameters:
|
||||
@ -301,11 +308,12 @@ final class TextFind {
|
||||
/// - replacementString: The string with which to replace.
|
||||
/// - block: The Block enumerates the matches.
|
||||
/// - flag: The current state of the replacing progress.
|
||||
/// - range: The matched range.
|
||||
/// - stop: The Block can set the value to true to stop further processing of the array.
|
||||
/// - Returns:
|
||||
/// - replacementItems: ReplacementItem per selectedRange.
|
||||
/// - selectedRanges: New selections for textView only if the replacement is performed within selection. Otherwise, nil.
|
||||
func replaceAll(with replacementString: String, using block: @escaping (_ flag: ReplacingFlag, _ stop: inout Bool) -> Void) -> (replacementItems: [ReplacementItem], selectedRanges: [NSRange]?) {
|
||||
func replaceAll(with replacementString: String, using block: @escaping (_ flag: ReplacingFlag, _ range: NSRange, _ stop: inout Bool) -> Void) -> (replacementItems: [ReplacementItem], selectedRanges: [NSRange]?) {
|
||||
|
||||
let replacementString = self.replacementString(from: replacementString)
|
||||
var replacementItems: [ReplacementItem] = []
|
||||
@ -324,7 +332,7 @@ final class TextFind {
|
||||
|
||||
items.append(ReplacementItem(string: replacedString, range: matchedRange))
|
||||
|
||||
block(.findProgress, &ioStop)
|
||||
block(.findProgress, matchedRange, &ioStop)
|
||||
stop = ioStop
|
||||
}
|
||||
|
||||
@ -338,7 +346,7 @@ final class TextFind {
|
||||
let replacedString = NSMutableString(string: (self.string as NSString).substring(with: scopeRange))
|
||||
for item in items.reversed() {
|
||||
var ioStop = false
|
||||
block(.replacementProgress, &ioStop)
|
||||
block(.replacementProgress, item.range, &ioStop)
|
||||
if ioStop { break }
|
||||
|
||||
// -> Do not convert to Range<Index>. It can fail when the range is smaller than String.Character.
|
||||
|
@ -301,7 +301,7 @@ final class TextFinder: NSResponder, NSMenuItemValidation {
|
||||
let replacementString = self.replacementString
|
||||
|
||||
// setup progress sheet
|
||||
let progress = FindProgress()
|
||||
let progress = FindProgress(scope: textFind.scopeRange)
|
||||
let closesAutomatically = UserDefaults.standard[.findClosesIndicatorWhenDone]
|
||||
let indicatorView = FindProgressView("Replace All", unit: .replacement, progress: progress)
|
||||
let indicator = NSHostingController(rootView: indicatorView)
|
||||
@ -311,7 +311,7 @@ final class TextFinder: NSResponder, NSMenuItemValidation {
|
||||
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
||||
guard let self else { return }
|
||||
|
||||
let (replacementItems, selectedRanges) = textFind.replaceAll(with: replacementString) { (flag, stop) in
|
||||
let (replacementItems, selectedRanges) = textFind.replaceAll(with: replacementString) { (flag, range, stop) in
|
||||
guard !progress.isCancelled else {
|
||||
stop = true
|
||||
return
|
||||
@ -321,6 +321,7 @@ final class TextFinder: NSResponder, NSMenuItemValidation {
|
||||
case .findProgress:
|
||||
break
|
||||
case .replacementProgress:
|
||||
progress.completedUnit = range.upperBound
|
||||
progress.count += 1
|
||||
}
|
||||
}
|
||||
@ -551,7 +552,7 @@ final class TextFinder: NSResponder, NSMenuItemValidation {
|
||||
let lineCounter = LineCounter(textFind.string as NSString)
|
||||
|
||||
// setup progress sheet
|
||||
let progress = FindProgress()
|
||||
let progress = FindProgress(scope: textFind.scopeRange)
|
||||
let closesAutomatically = UserDefaults.standard[.findClosesIndicatorWhenDone]
|
||||
let indicatorView = FindProgressView(actionName, unit: .find, progress: progress)
|
||||
let indicator = NSHostingController(rootView: indicatorView)
|
||||
@ -599,6 +600,7 @@ final class TextFinder: NSResponder, NSMenuItemValidation {
|
||||
results.append(TextFindResult(range: matchedRange, lineNumber: lineNumber, attributedLineString: attrLineString, inlineRange: inlineRange))
|
||||
}
|
||||
|
||||
progress.completedUnit = matches[0].upperBound
|
||||
progress.count += 1
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user