Skip to content

Commit

Permalink
Merge pull request #78 from DmIvanov/Sorting
Browse files Browse the repository at this point in the history
Sorting by filename
  • Loading branch information
Robert Gummesson authored Dec 2, 2017
2 parents b502402 + 84db64f commit e860691
Show file tree
Hide file tree
Showing 6 changed files with 316 additions and 41 deletions.
8 changes: 8 additions & 0 deletions BuildTimeAnalyzer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
2839B8691FD2896F004C075C /* ViewControllerDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2839B8681FD2896F004C075C /* ViewControllerDataSource.swift */; };
2839B86B1FD32766004C075C /* ViewControllerDataSourceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2839B86A1FD32766004C075C /* ViewControllerDataSourceTest.swift */; };
2A3164C81D21D73F00064045 /* CompileMeasure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3164C01D21D73F00064045 /* CompileMeasure.swift */; };
2A3164C91D21D73F00064045 /* LogProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3164C11D21D73F00064045 /* LogProcessor.swift */; };
2A3164CB1D21D73F00064045 /* ProcessingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3164C31D21D73F00064045 /* ProcessingState.swift */; };
Expand Down Expand Up @@ -39,6 +41,8 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
2839B8681FD2896F004C075C /* ViewControllerDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerDataSource.swift; sourceTree = "<group>"; };
2839B86A1FD32766004C075C /* ViewControllerDataSourceTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerDataSourceTest.swift; sourceTree = "<group>"; };
2A3164C01D21D73F00064045 /* CompileMeasure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompileMeasure.swift; sourceTree = "<group>"; };
2A3164C11D21D73F00064045 /* LogProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogProcessor.swift; sourceTree = "<group>"; };
2A3164C31D21D73F00064045 /* ProcessingState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProcessingState.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -142,6 +146,7 @@
isa = PBXGroup;
children = (
2A3698AB1D80A33B002C5CDA /* ViewController.swift */,
2839B8681FD2896F004C075C /* ViewControllerDataSource.swift */,
);
name = ViewControllers;
sourceTree = "<group>";
Expand Down Expand Up @@ -196,6 +201,7 @@
isa = PBXGroup;
children = (
2A3164CF1D21D74A00064045 /* CompileMeasureTests.swift */,
2839B86A1FD32766004C075C /* ViewControllerDataSourceTest.swift */,
2A3164D71D21D7A800064045 /* Supporting Files */,
);
path = BuildTimeAnalyzerTests;
Expand Down Expand Up @@ -306,6 +312,7 @@
2A9807DD1D7C71F900B9232C /* DirectoryMonitor.swift in Sources */,
2A3164DA1D21D90100064045 /* NSData+GZIP.m in Sources */,
2A3164C91D21D73F00064045 /* LogProcessor.swift in Sources */,
2839B8691FD2896F004C075C /* ViewControllerDataSource.swift in Sources */,
2A5404011D86D01700DBD44C /* BuildManager.swift in Sources */,
2A5404051D86F3C700DBD44C /* File.swift in Sources */,
2ABFB6CE1D81F2DE00D060BF /* NSAlert+Extensions.swift in Sources */,
Expand All @@ -327,6 +334,7 @@
files = (
2A3164D51D21D77500064045 /* NSData+GZIP.m in Sources */,
2A3164D01D21D74A00064045 /* CompileMeasureTests.swift in Sources */,
2839B86B1FD32766004C075C /* ViewControllerDataSourceTest.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
11 changes: 8 additions & 3 deletions BuildTimeAnalyzer/CompileMeasure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@

import Foundation

struct CompileMeasure {
@objcMembers class CompileMeasure: NSObject {

var time: Double
dynamic var time: Double
var path: String
var code: String
var filename: String
dynamic var filename: String
var references: Int

private var locationArray: [Int]

public enum Order: String {
case filename
case time
}

var fileAndLine: String {
return "\(filename):\(locationArray[0])"
}
Expand Down
4 changes: 2 additions & 2 deletions BuildTimeAnalyzer/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -527,15 +527,15 @@ Gw
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Zkv-Tf-sdq">
<rect key="frame" x="190" y="305" width="226" height="17"/>
<rect key="frame" x="190" y="305" width="225" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="2) Clean your project (⌘ + Shift + K)" id="eMR-lR-OuM">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PV7-TD-diE">
<rect key="frame" x="190" y="253" width="340" height="17"/>
<rect key="frame" x="190" y="253" width="339" height="17"/>
<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">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
Expand Down
56 changes: 20 additions & 36 deletions BuildTimeAnalyzer/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,13 @@ class ViewController: NSViewController {
@IBOutlet weak var statusTextField: NSTextField!
@IBOutlet weak var tableView: NSTableView!
@IBOutlet weak var tableViewContainerView: NSScrollView!

fileprivate var dataSource: [CompileMeasure] = []
fileprivate var filteredData: [CompileMeasure]?

fileprivate let dataSource = ViewControllerDataSource()

private var currentKey: String?
private var nextDatabase: XcodeDatabase?

private var processor = LogProcessor()
private var perFunctionTimes: [CompileMeasure] = []
private var perFileTimes: [CompileMeasure] = []

var processingState: ProcessingState = .waiting {
didSet {
Expand All @@ -48,7 +45,10 @@ class ViewController: NSViewController {
buildManager.delegate = self
projectSelection.delegate = self
projectSelection.listFolders()


tableView.tableColumns[0].sortDescriptorPrototype = NSSortDescriptor(key: CompileMeasure.Order.time.rawValue, ascending: true)
tableView.tableColumns[1].sortDescriptorPrototype = NSSortDescriptor(key: CompileMeasure.Order.filename.rawValue, ascending: true)

NotificationCenter.default.addObserver(self, selector: #selector(windowWillClose(notification:)), name: NSWindow.willCloseNotification, object: nil)
}

Expand Down Expand Up @@ -99,21 +99,6 @@ class ViewController: NSViewController {
}
}

func aggregateTimesByFile(_ functionTimes: [CompileMeasure]) -> [CompileMeasure] {
var fileTimes: [String: CompileMeasure] = [:]

for measure in functionTimes {
if var fileMeasure = fileTimes[measure.path] {
fileMeasure.time += measure.time
fileTimes[measure.path] = fileMeasure
} else {
let newFileMeasure = CompileMeasure(rawPath: measure.path, time: measure.time)
fileTimes[measure.path] = newFileMeasure
}
}
return Array(fileTimes.values).sorted{ $0.time > $1.time }
}

func updateViewForState() {
switch processingState {
case .processing:
Expand Down Expand Up @@ -151,7 +136,7 @@ class ViewController: NSViewController {
// MARK: Actions

@IBAction func perFileCheckboxClicked(_ sender: NSButton) {
dataSource = sender.state.rawValue == 0 ? perFunctionTimes : perFileTimes
dataSource.aggregateByFile = (sender.state.rawValue == 1)
tableView.reloadData()
}

Expand Down Expand Up @@ -179,7 +164,7 @@ class ViewController: NSViewController {

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

func handleProcessorUpdate(result: [CompileMeasure], didComplete: Bool, didCancel: Bool) {
dataSource = result
perFunctionTimes = result
perFileTimes = aggregateTimesByFile(perFunctionTimes)
dataSource.resetSourceData(newSourceData: result)
tableView.reloadData()

if didComplete {
Expand All @@ -238,7 +221,7 @@ class ViewController: NSViewController {
}

func completeProcessorUpdate(didCancel: Bool) {
let didSucceed = !dataSource.isEmpty
let didSucceed = !dataSource.isEmpty()

var stateName = ProcessingState.failedString
if didCancel {
Expand Down Expand Up @@ -268,23 +251,18 @@ class ViewController: NSViewController {
let text = "Build duration: " + (buildTime < 60 ? "\(buildTime)s" : "\(buildTime / 60)m \(buildTime % 60)s")
compileTimeTextField.stringValue = text
}

func textContains(_ text: String) -> Bool {
return text.lowercased().contains(searchField.stringValue.lowercased())
}
}

// MARK: NSTableViewDataSource

extension ViewController: NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return filteredData?.count ?? dataSource.count
return dataSource.count()
}

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


let gotoLineScript =
"tell application \"Xcode\"\n" +
Expand All @@ -311,12 +289,18 @@ extension ViewController: NSTableViewDataSource {
extension ViewController: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
guard let tableColumn = tableColumn, let columnIndex = tableView.tableColumns.index(of: tableColumn) else { return nil }

guard let item = dataSource.measure(index: row) else { return nil }

let result = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell\(columnIndex)"), owner: self) as? NSTableCellView
result?.textField?.stringValue = filteredData?[row][columnIndex] ?? dataSource[row][columnIndex]
result?.textField?.stringValue = item[columnIndex]

return result
}

func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
dataSource.sortDescriptors = tableView.sortDescriptors
tableView.reloadData()
}
}

// MARK: BuildManagerDelegate
Expand Down
90 changes: 90 additions & 0 deletions BuildTimeAnalyzer/ViewControllerDataSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// ViewControllerDataSource.swift
// BuildTimeAnalyzer
//
// Created by Dmitrii on 02/12/2017.
// Copyright © 2017 Cane Media Ltd. All rights reserved.
//

import Foundation

class ViewControllerDataSource {

var aggregateByFile = false {
didSet {
processData()
}
}

var filter = "" {
didSet {
processData()
}
}

var sortDescriptors = [NSSortDescriptor]() {
didSet {
processData()
}
}

fileprivate var originalData = [CompileMeasure]()
fileprivate var processedData = [CompileMeasure]()

func resetSourceData(newSourceData: [CompileMeasure]) {
originalData = newSourceData
processData()
}

func isEmpty() -> Bool {
return processedData.isEmpty
}

func count() -> Int {
return processedData.count
}

func measure(index: Int) -> CompileMeasure? {
guard index < processedData.count && index >= 0 else { return nil }
return processedData[index]
}

// MARK: - Private methods

private func processData() {
var newProcessedData = aggregateIfNeeded(originalData)
newProcessedData = applySortingIfNeeded(newProcessedData)
newProcessedData = applyFilteringIfNeeded(newProcessedData)

processedData = newProcessedData
}

private func aggregateIfNeeded(_ input: [CompileMeasure]) -> [CompileMeasure] {
guard aggregateByFile else { return input }
var fileTimes: [String: CompileMeasure] = [:]
for measure in input {
if let fileMeasure = fileTimes[measure.path] {
fileMeasure.time += measure.time
fileTimes[measure.path] = fileMeasure
} else {
let newFileMeasure = CompileMeasure(rawPath: measure.path, time: measure.time)
fileTimes[measure.path] = newFileMeasure
}
}
return Array(fileTimes.values)
}

private func applySortingIfNeeded(_ input: [CompileMeasure]) -> [CompileMeasure] {
if sortDescriptors.isEmpty { return input }
return (input as NSArray).sortedArray(using: sortDescriptors) as! Array
}

private func applyFilteringIfNeeded(_ input: [CompileMeasure]) -> [CompileMeasure] {
guard !filter.isEmpty else { return input }
return input.filter{ textContains($0.code, pattern: filter) || textContains($0.filename, pattern: filter) }
}

private func textContains(_ text: String, pattern: String) -> Bool {
return text.lowercased().contains(pattern.lowercased())
}
}
Loading

0 comments on commit e860691

Please sign in to comment.