Skip to content

Commit e860691

Browse files
author
Robert Gummesson
authored
Merge pull request #78 from DmIvanov/Sorting
Sorting by filename
2 parents b502402 + 84db64f commit e860691

File tree

6 files changed

+316
-41
lines changed

6 files changed

+316
-41
lines changed

BuildTimeAnalyzer.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
2839B8691FD2896F004C075C /* ViewControllerDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2839B8681FD2896F004C075C /* ViewControllerDataSource.swift */; };
11+
2839B86B1FD32766004C075C /* ViewControllerDataSourceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2839B86A1FD32766004C075C /* ViewControllerDataSourceTest.swift */; };
1012
2A3164C81D21D73F00064045 /* CompileMeasure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3164C01D21D73F00064045 /* CompileMeasure.swift */; };
1113
2A3164C91D21D73F00064045 /* LogProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3164C11D21D73F00064045 /* LogProcessor.swift */; };
1214
2A3164CB1D21D73F00064045 /* ProcessingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3164C31D21D73F00064045 /* ProcessingState.swift */; };
@@ -39,6 +41,8 @@
3941
/* End PBXContainerItemProxy section */
4042

4143
/* Begin PBXFileReference section */
44+
2839B8681FD2896F004C075C /* ViewControllerDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerDataSource.swift; sourceTree = "<group>"; };
45+
2839B86A1FD32766004C075C /* ViewControllerDataSourceTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerDataSourceTest.swift; sourceTree = "<group>"; };
4246
2A3164C01D21D73F00064045 /* CompileMeasure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompileMeasure.swift; sourceTree = "<group>"; };
4347
2A3164C11D21D73F00064045 /* LogProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogProcessor.swift; sourceTree = "<group>"; };
4448
2A3164C31D21D73F00064045 /* ProcessingState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProcessingState.swift; sourceTree = "<group>"; };
@@ -142,6 +146,7 @@
142146
isa = PBXGroup;
143147
children = (
144148
2A3698AB1D80A33B002C5CDA /* ViewController.swift */,
149+
2839B8681FD2896F004C075C /* ViewControllerDataSource.swift */,
145150
);
146151
name = ViewControllers;
147152
sourceTree = "<group>";
@@ -196,6 +201,7 @@
196201
isa = PBXGroup;
197202
children = (
198203
2A3164CF1D21D74A00064045 /* CompileMeasureTests.swift */,
204+
2839B86A1FD32766004C075C /* ViewControllerDataSourceTest.swift */,
199205
2A3164D71D21D7A800064045 /* Supporting Files */,
200206
);
201207
path = BuildTimeAnalyzerTests;
@@ -306,6 +312,7 @@
306312
2A9807DD1D7C71F900B9232C /* DirectoryMonitor.swift in Sources */,
307313
2A3164DA1D21D90100064045 /* NSData+GZIP.m in Sources */,
308314
2A3164C91D21D73F00064045 /* LogProcessor.swift in Sources */,
315+
2839B8691FD2896F004C075C /* ViewControllerDataSource.swift in Sources */,
309316
2A5404011D86D01700DBD44C /* BuildManager.swift in Sources */,
310317
2A5404051D86F3C700DBD44C /* File.swift in Sources */,
311318
2ABFB6CE1D81F2DE00D060BF /* NSAlert+Extensions.swift in Sources */,
@@ -327,6 +334,7 @@
327334
files = (
328335
2A3164D51D21D77500064045 /* NSData+GZIP.m in Sources */,
329336
2A3164D01D21D74A00064045 /* CompileMeasureTests.swift in Sources */,
337+
2839B86B1FD32766004C075C /* ViewControllerDataSourceTest.swift in Sources */,
330338
);
331339
runOnlyForDeploymentPostprocessing = 0;
332340
};

BuildTimeAnalyzer/CompileMeasure.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,21 @@
55

66
import Foundation
77

8-
struct CompileMeasure {
8+
@objcMembers class CompileMeasure: NSObject {
99

10-
var time: Double
10+
dynamic var time: Double
1111
var path: String
1212
var code: String
13-
var filename: String
13+
dynamic var filename: String
1414
var references: Int
1515

1616
private var locationArray: [Int]
1717

18+
public enum Order: String {
19+
case filename
20+
case time
21+
}
22+
1823
var fileAndLine: String {
1924
return "\(filename):\(locationArray[0])"
2025
}

BuildTimeAnalyzer/Main.storyboard

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,15 +527,15 @@ Gw
527527
</textFieldCell>
528528
</textField>
529529
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Zkv-Tf-sdq">
530-
<rect key="frame" x="190" y="305" width="226" height="17"/>
530+
<rect key="frame" x="190" y="305" width="225" height="17"/>
531531
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="2) Clean your project (⌘ + Shift + K)" id="eMR-lR-OuM">
532532
<font key="font" metaFont="system"/>
533533
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
534534
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
535535
</textFieldCell>
536536
</textField>
537537
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PV7-TD-diE">
538-
<rect key="frame" x="190" y="253" width="340" height="17"/>
538+
<rect key="frame" x="190" y="253" width="339" height="17"/>
539539
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="3) Build your project (⌘ + B) and wait for it to complete" id="WcG-TO-qa4">
540540
<font key="font" metaFont="system"/>
541541
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>

BuildTimeAnalyzer/ViewController.swift

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,13 @@ class ViewController: NSViewController {
2121
@IBOutlet weak var statusTextField: NSTextField!
2222
@IBOutlet weak var tableView: NSTableView!
2323
@IBOutlet weak var tableViewContainerView: NSScrollView!
24-
25-
fileprivate var dataSource: [CompileMeasure] = []
26-
fileprivate var filteredData: [CompileMeasure]?
24+
25+
fileprivate let dataSource = ViewControllerDataSource()
2726

2827
private var currentKey: String?
2928
private var nextDatabase: XcodeDatabase?
3029

3130
private var processor = LogProcessor()
32-
private var perFunctionTimes: [CompileMeasure] = []
33-
private var perFileTimes: [CompileMeasure] = []
3431

3532
var processingState: ProcessingState = .waiting {
3633
didSet {
@@ -48,7 +45,10 @@ class ViewController: NSViewController {
4845
buildManager.delegate = self
4946
projectSelection.delegate = self
5047
projectSelection.listFolders()
51-
48+
49+
tableView.tableColumns[0].sortDescriptorPrototype = NSSortDescriptor(key: CompileMeasure.Order.time.rawValue, ascending: true)
50+
tableView.tableColumns[1].sortDescriptorPrototype = NSSortDescriptor(key: CompileMeasure.Order.filename.rawValue, ascending: true)
51+
5252
NotificationCenter.default.addObserver(self, selector: #selector(windowWillClose(notification:)), name: NSWindow.willCloseNotification, object: nil)
5353
}
5454

@@ -99,21 +99,6 @@ class ViewController: NSViewController {
9999
}
100100
}
101101

102-
func aggregateTimesByFile(_ functionTimes: [CompileMeasure]) -> [CompileMeasure] {
103-
var fileTimes: [String: CompileMeasure] = [:]
104-
105-
for measure in functionTimes {
106-
if var fileMeasure = fileTimes[measure.path] {
107-
fileMeasure.time += measure.time
108-
fileTimes[measure.path] = fileMeasure
109-
} else {
110-
let newFileMeasure = CompileMeasure(rawPath: measure.path, time: measure.time)
111-
fileTimes[measure.path] = newFileMeasure
112-
}
113-
}
114-
return Array(fileTimes.values).sorted{ $0.time > $1.time }
115-
}
116-
117102
func updateViewForState() {
118103
switch processingState {
119104
case .processing:
@@ -151,7 +136,7 @@ class ViewController: NSViewController {
151136
// MARK: Actions
152137

153138
@IBAction func perFileCheckboxClicked(_ sender: NSButton) {
154-
dataSource = sender.state.rawValue == 0 ? perFunctionTimes : perFileTimes
139+
dataSource.aggregateByFile = (sender.state.rawValue == 1)
155140
tableView.reloadData()
156141
}
157142

@@ -179,7 +164,7 @@ class ViewController: NSViewController {
179164

180165
override func controlTextDidChange(_ obj: Notification) {
181166
if let field = obj.object as? NSSearchField, field == searchField {
182-
filteredData = field.stringValue.isEmpty ? nil : dataSource.filter{ textContains($0.code) || textContains($0.filename) }
167+
dataSource.filter = searchField.stringValue
183168
tableView.reloadData()
184169
} else if let field = obj.object as? NSTextField, field == derivedDataTextField {
185170
buildManager.stopMonitoring()
@@ -227,9 +212,7 @@ class ViewController: NSViewController {
227212
}
228213

229214
func handleProcessorUpdate(result: [CompileMeasure], didComplete: Bool, didCancel: Bool) {
230-
dataSource = result
231-
perFunctionTimes = result
232-
perFileTimes = aggregateTimesByFile(perFunctionTimes)
215+
dataSource.resetSourceData(newSourceData: result)
233216
tableView.reloadData()
234217

235218
if didComplete {
@@ -238,7 +221,7 @@ class ViewController: NSViewController {
238221
}
239222

240223
func completeProcessorUpdate(didCancel: Bool) {
241-
let didSucceed = !dataSource.isEmpty
224+
let didSucceed = !dataSource.isEmpty()
242225

243226
var stateName = ProcessingState.failedString
244227
if didCancel {
@@ -268,23 +251,18 @@ class ViewController: NSViewController {
268251
let text = "Build duration: " + (buildTime < 60 ? "\(buildTime)s" : "\(buildTime / 60)m \(buildTime % 60)s")
269252
compileTimeTextField.stringValue = text
270253
}
271-
272-
func textContains(_ text: String) -> Bool {
273-
return text.lowercased().contains(searchField.stringValue.lowercased())
274-
}
275254
}
276255

277256
// MARK: NSTableViewDataSource
278257

279258
extension ViewController: NSTableViewDataSource {
280259
func numberOfRows(in tableView: NSTableView) -> Int {
281-
return filteredData?.count ?? dataSource.count
260+
return dataSource.count()
282261
}
283262

284263
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
285-
let item = filteredData?[row] ?? dataSource[row]
264+
guard let item = dataSource.measure(index: row) else { return false }
286265
NSWorkspace.shared.openFile(item.path)
287-
288266

289267
let gotoLineScript =
290268
"tell application \"Xcode\"\n" +
@@ -311,12 +289,18 @@ extension ViewController: NSTableViewDataSource {
311289
extension ViewController: NSTableViewDelegate {
312290
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
313291
guard let tableColumn = tableColumn, let columnIndex = tableView.tableColumns.index(of: tableColumn) else { return nil }
314-
292+
guard let item = dataSource.measure(index: row) else { return nil }
293+
315294
let result = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell\(columnIndex)"), owner: self) as? NSTableCellView
316-
result?.textField?.stringValue = filteredData?[row][columnIndex] ?? dataSource[row][columnIndex]
295+
result?.textField?.stringValue = item[columnIndex]
317296

318297
return result
319298
}
299+
300+
func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
301+
dataSource.sortDescriptors = tableView.sortDescriptors
302+
tableView.reloadData()
303+
}
320304
}
321305

322306
// MARK: BuildManagerDelegate
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//
2+
// ViewControllerDataSource.swift
3+
// BuildTimeAnalyzer
4+
//
5+
// Created by Dmitrii on 02/12/2017.
6+
// Copyright © 2017 Cane Media Ltd. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
class ViewControllerDataSource {
12+
13+
var aggregateByFile = false {
14+
didSet {
15+
processData()
16+
}
17+
}
18+
19+
var filter = "" {
20+
didSet {
21+
processData()
22+
}
23+
}
24+
25+
var sortDescriptors = [NSSortDescriptor]() {
26+
didSet {
27+
processData()
28+
}
29+
}
30+
31+
fileprivate var originalData = [CompileMeasure]()
32+
fileprivate var processedData = [CompileMeasure]()
33+
34+
func resetSourceData(newSourceData: [CompileMeasure]) {
35+
originalData = newSourceData
36+
processData()
37+
}
38+
39+
func isEmpty() -> Bool {
40+
return processedData.isEmpty
41+
}
42+
43+
func count() -> Int {
44+
return processedData.count
45+
}
46+
47+
func measure(index: Int) -> CompileMeasure? {
48+
guard index < processedData.count && index >= 0 else { return nil }
49+
return processedData[index]
50+
}
51+
52+
// MARK: - Private methods
53+
54+
private func processData() {
55+
var newProcessedData = aggregateIfNeeded(originalData)
56+
newProcessedData = applySortingIfNeeded(newProcessedData)
57+
newProcessedData = applyFilteringIfNeeded(newProcessedData)
58+
59+
processedData = newProcessedData
60+
}
61+
62+
private func aggregateIfNeeded(_ input: [CompileMeasure]) -> [CompileMeasure] {
63+
guard aggregateByFile else { return input }
64+
var fileTimes: [String: CompileMeasure] = [:]
65+
for measure in input {
66+
if let fileMeasure = fileTimes[measure.path] {
67+
fileMeasure.time += measure.time
68+
fileTimes[measure.path] = fileMeasure
69+
} else {
70+
let newFileMeasure = CompileMeasure(rawPath: measure.path, time: measure.time)
71+
fileTimes[measure.path] = newFileMeasure
72+
}
73+
}
74+
return Array(fileTimes.values)
75+
}
76+
77+
private func applySortingIfNeeded(_ input: [CompileMeasure]) -> [CompileMeasure] {
78+
if sortDescriptors.isEmpty { return input }
79+
return (input as NSArray).sortedArray(using: sortDescriptors) as! Array
80+
}
81+
82+
private func applyFilteringIfNeeded(_ input: [CompileMeasure]) -> [CompileMeasure] {
83+
guard !filter.isEmpty else { return input }
84+
return input.filter{ textContains($0.code, pattern: filter) || textContains($0.filename, pattern: filter) }
85+
}
86+
87+
private func textContains(_ text: String, pattern: String) -> Bool {
88+
return text.lowercased().contains(pattern.lowercased())
89+
}
90+
}

0 commit comments

Comments
 (0)