Showing preview only (448K chars total). Download the full file or copy to clipboard to get everything.
Repository: johnno1962/RefactoratorApp
Branch: master
Commit: 4c41d15f1d87
Files: 32
Total size: 432.4 KB
Directory structure:
gitextract_cm_vpaff/
├── .gitignore
├── LICENSE
├── README.md
├── Refactorator/
│ ├── App.icns
│ ├── AppController.swift
│ ├── AppDelegate.swift
│ ├── Assets.xcassets/
│ │ └── AppIcon.appiconset/
│ │ └── Contents.json
│ ├── Base.lproj/
│ │ └── MainMenu.xib
│ ├── Common.swift
│ ├── Coverage.swift
│ ├── Credits.rtf
│ ├── Formatter.swift
│ ├── Info.plist
│ ├── Integration.swift
│ ├── Intro.html
│ ├── Project.swift
│ ├── Refactorator-Bridging-Header.h
│ ├── Source.html
│ ├── Utils.h
│ ├── Utils.m
│ ├── Xcode.h
│ ├── canviz.html
│ ├── canviz2.html
│ └── coverage.rb
├── Refactorator.xcodeproj/
│ ├── project.pbxproj
│ └── project.xcworkspace/
│ └── contents.xcworkspacedata
└── canviz-0.1/
├── LICENSE.txt
├── README.txt
├── canviz.css
├── canviz.js
├── path/
│ └── path.js
└── prototype/
└── prototype.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*html/*
*xcuserdata*
*xccheckout*
================================================
FILE: LICENSE
================================================
Copyright (c) 2017 John Holdsworth
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# Refactorator II, The App
Due to chnages in the index databaase format in Xcode 10, this program no longer works properly.
This is the source for the App version of the [Refactorator plugin](https://github.com/johnno1962/Refactorator)
for refactoring Swift (well, renaming actually.)
One of the features of app is it can convert a project into a standalone website of navigable code for
example [this project](http://johnholdsworth.com/refactorator).
To build, clone this *and the code for the Refactorator plugin* next to each other the build this project.
Help is available [here](http://johnholdsworth.com/refactorator.html) where you can download a [pre-built
binary](http://johnholdsworth.com/Refactorator.app.zip).

The project now draws interactive dependecncy graphs between sources in a project if you have Graphviz installed.

Refactorator now takes into account membership of protocols and overrides in the main target
if you augment the index db by using the "File/Index Protocols" menu item.
This project is an experiment in "Beerware" there is a "donate" button on the Help Menu ;)
## MIT Licensed
Copyright (C) 2016 John Holdsworth refactorator@johnholdsworth.com [@Injection4Xcode](https://twitter.com/@Injection4Xcode)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This release includes the canviz, path amd prototype JavaScript libraries which are distributed under the terms of their respective licenses.
================================================
FILE: Refactorator/AppController.swift
================================================
//
// AppState.swift
// Refactorator
//
// Created by John Holdsworth on 13/12/2016.
// Copyright © 2016 John Holdsworth. All rights reserved.
//
import Foundation
import WebKit
class AppController: NSObject, AppGui, WebUIDelegate, WebFrameLoadDelegate, WebPolicyDelegate {
@IBOutlet weak var window: NSWindow!
@IBOutlet weak var sourceView: WebView!
@IBOutlet weak var changesView: WebView!
weak var printWebView: WebView!
let canvizEntity = Entity(file: "[Dependencies]")
var canvizView: WebView!
@IBOutlet weak var replacement: NSTextField!
@IBOutlet weak var findText: NSTextField!
@IBOutlet var backItem: NSMenuItem!
@IBOutlet var forwardItem: NSMenuItem!
@IBOutlet var reloadItem: NSMenuItem!
@IBOutlet var dependsItem: NSMenuItem!
@IBOutlet var searchItem: NSMenuItem!
var history = [Entity]()
var future = [Entity]()
var project: Project?
var html = "", oldValue = "", changes = ""
var wasSearch = false
var entitiesByFile = [[Entity]]()
var originals = [String:NSData]()
var modified = [String:NSData]()
var linecounts = [String:Int]()
var formatter = Formatter()
func log( _ msg: String ) {
appendSource(title: "", text: "<div class=log>\(msg)</div>")
Swift.print( msg )
}
func error( _ msg: String ) {
window.title = msg
log( "<div class=error>\(msg)</div>" )
}
func open( url: String ) {
NSWorkspace.shared().open(url.url)
}
func sourceHTML() -> String {
let path = Bundle.main.path(forResource: "Source", ofType: "html")!
return try! String(contentsOfFile: path, encoding:.utf8)
}
func defaultEntity() -> Entity {
if let recentSource = NSDocumentController.shared().recentDocumentURLs.first {
let entity = Entity(file: recentSource.path)
project = Project(target: entity)
return entity
}
return Entity(file: Bundle.main.path(forResource: "Intro", ofType: "html")!)
}
@discardableResult
func setupChanges() -> String {
let code = sourceHTML()
if changesView.uiDelegate == nil {
changesView.uiDelegate = self
changesView.frameLoadDelegate = self
changesView.mainFrame.loadHTMLString(code+"<div>Click on a symbol to locate references to rename</div>", baseURL: nil)
changesView.policyDelegate = self
RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.1))
changesView.windowScriptObject.setValue(self, forKey:"appController2")
}
return code
}
func setup( target: Entity? = nil, cascade: Bool = true ) {
let code = setupChanges()
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(nil)
if target != canvizEntity {
project = Project(target: target)
canvizView?.removeFromSuperview()
}
else {
if canvizView == nil {
canvizView = WebView(frame: sourceView.frame)
canvizView.autoresizingMask = sourceView.autoresizingMask
canvizView.frameLoadDelegate = self
canvizView.uiDelegate = self
let path = Bundle.main.path(forResource: "canviz", ofType: "html")!
canvizView.mainFrame.load( URLRequest( url: path.url ) )
}
else {
canvizView.frame = sourceView.frame
}
sourceView.superview?.addSubview(canvizView)
history.append( canvizEntity )
return
}
let target = target ?? project?.entity ?? defaultEntity()
if printWebView == nil {
printWebView = sourceView
}
setLocation(entity: target)
future.removeAll()
if let sourceData = NSData(contentsOfFile: target.file) {
if target.sourceName == "Intro.html" {
html = String(data:sourceData as Data, encoding:.utf8)!
}
else {
let entities = project?.indexDB?.entitiesFor(filePath: target.file)
let coverage = project != nil ? Coverage(file: target.file, project: project!)?.covered : nil
html = formatter.htmlFor(path: target.file, data: sourceData, entities: entities,
selecting: target, cascade: cascade, cleanPath: relative(target.file),
coverage: coverage).joined()
}
if sourceView.uiDelegate == nil {
sourceView.uiDelegate = self
sourceView.frameLoadDelegate = self
sourceView.mainFrame.loadHTMLString(code, baseURL: nil)
}
else {
sourceView.windowScriptObject.callWebScriptMethod("setSource", withArguments: [html])
}
}
else {
xcode.error("Could not open \(target.file)")
}
}
func setLocation( entity: Entity ) {
if entity != history.last || entity.offset != history.last?.offset {
history.append( entity )
}
if entity.sourceName == "Intro.html" {
return
}
let sourceURL = entity.file.url
NSDocumentController.shared().noteNewRecentDocumentURL(sourceURL)
let sourceDir = sourceURL.deletingLastPathComponent().path
if var workspace = project?.workspaceName {
if !IndexDB.projectIncludes(file: entity.file) {
workspace = "Incorrect frontmost open workspace \(workspace)"
}
window.title = "\(sourceURL.lastPathComponent) – \(sourceDir.replacingOccurrences(of: HOME, with: "~")) – \(workspace)"
}
}
@objc func webView( _ webView: WebView, addMessageToConsole message: NSDictionary ) {
Swift.print("\(message)")
}
@objc func webView(_ sender: WebView!, runJavaScriptAlertPanelWithMessage message: String!, initiatedBy frame: WebFrame!) {
print("\(message)")
}
@objc func webView(_ sender: WebView!, didFinishLoadFor frame: WebFrame!) {
guard let win = sender.windowScriptObject else { return }
if sender == sourceView {
sourceView.policyDelegate = self
win.setValue(self, forKey:"appController")
win.callWebScriptMethod("setSource", withArguments: [html])
}
else if sender == changesView {
win.setValue(self, forKey:"appController2")
}
else if sender == canvizView {
win.setValue(self, forKey:"appController")
}
}
@objc func webView(_ webView: WebView!, decidePolicyForNavigationAction actionInformation: [AnyHashable : Any]!,
request: URLRequest!, frame: WebFrame!, decisionListener listener: WebPolicyDecisionListener!) {
if request.url!.scheme != "about" {
NSWorkspace.shared().open(request.url!)
listener.ignore()
}
else {
listener.use()
}
}
@objc func webView(_ sender: WebView!, contextMenuItemsForElement element: [AnyHashable : Any]!, defaultMenuItems: [Any]!) -> [Any]! {
return [backItem, forwardItem, reloadItem, dependsItem, searchItem]
}
func setChangesSource( header: String? = nil, target: Entity? = nil, isApply: Bool = false ) {
window.makeKeyAndOrderFront(nil)
if changesView.uiDelegate == nil {
setupChanges()
RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.1))
}
if !isApply, let last = history.last, last != canvizEntity {
project = Project(target: target ?? last)
}
let args = [header != nil ? "<div class=changesHeader>\(header!)</div>" : ""]
changesView.windowScriptObject.setValue(self, forKey:"appController2")
changesView.windowScriptObject.callWebScriptMethod("setSource", withArguments: args)
if project?.indexDB == nil {
xcode.error("No index DB found for project: \(project?.workspacePath ?? "unavailable")")
}
}
func appendSource( title: String, text: String ) {
changesView.windowScriptObject.callWebScriptMethod("appendSource", withArguments: [title, text])
}
func relative( _ path: String ) -> String {
return project != nil ? path
.replacingOccurrences(of: project!.projectRoot+"/", with: "")
.replacingOccurrences(of: HOME+"/", with: "") : path
}
@objc override class func isSelectorExcluded( fromWebScript aSelector: Selector ) -> Bool {
return
aSelector != #selector(selected(text:title:line:col:offset:metaKey:)) &&
aSelector != #selector(changeSelected(text:title:line:col:offset:metaKey:)) &&
aSelector != #selector(depends(path:)) &&
aSelector != #selector(graphvizExport) &&
aSelector != #selector(goto(file:line:))
}
@objc func goto(file: String, line: Int) {
setup(target: Entity(file: file))
sourceView.windowScriptObject.callWebScriptMethod("gotoLine", withArguments: ["\(line)"])
}
@objc public func selected( text: String, title: String, line: Int, col: Int, offset: Int, metaKey: Bool ) {
let entity = Entity(file: history.last?.file ?? title.components(separatedBy: "#")[0],
line: line, col: col, offset: offset)
setChangesSource(target: entity)
replacement.stringValue = text
printWebView = sourceView
entitiesByFile.removeAll()
oldValue = text
if project?.indexDB == nil {
xcode.error("Unable to open index db. Best guess at path was:\n\(project!.indexPath)")
return
}
let sourcePath = entity.file
if !IndexDB.projectIncludes(file: sourcePath) {
xcode.error("File does not seem to be in the project. Is the wrong project open in the frontmost window of Xcode?\n\n")
}
if let indexDB = project?.indexDB {
if let usrIDs = indexDB.usrIDsFor(filePath: sourcePath, line: line, col: col) {
if metaKey, let entity = indexDB.declarationFor(filePath: sourcePath, line: line, col: col) {
setup(target: entity, cascade: false)
return
}
setLocation(entity: entity)
let usrs = usrIDs.map { IndexDB.resolutions[$0] ?? "??\($0)" }
let usrText = usrs.sorted { demangle( $0 )! < demangle( $1 )! }
.map { "USR: <span title=\"\($0)\">\(htmlEscape( demangle( $0 ) ))</span>\n" }.joined()
appendSource(title: project!.indexPath, text: "<div class=usr>\(usrText)</div>" )
var system = false
var pathSeen = [String:Int]()
_ = indexDB.entitiesFor(usrIDs: usrIDs) {
(entities) in
let path = entities[0].file
if let seen = pathSeen[path] {
xcode.log("Already seen \(path) \(seen) times")
pathSeen[path] = seen + 1
}
else {
pathSeen[path] = 1
}
if path.contains("/Developer/Platforms/") ||
path.contains("/Developer/Toolchains/" ) {
system = true
// return
}
entitiesByFile.append( entities )
}
processEntities(type: "references")
if system {
appendSource(title: "", text: "\nToolchain symbol")
}
}
else {
xcode.log("<span title=\"\(project?.indexPath ?? "")\">No USR associated with \(entity.sourceName)#\(line):\(col) in project: \(project!.workspaceName). Is indexing complete?</span>")
Process.run(path: "/usr/bin/touch", args: [entity.file])
}
}
else {
xcode.error("Could load load index db for project \(project?.workspacePath ?? "unknown")")
}
}
@objc public func changeSelected( text: String, title: String, line: Int, col: Int, offset: Int, metaKey: Bool ) {
let sourcePath = title.components(separatedBy: "#")[0]
printWebView = changesView
if !metaKey {
setup(target: Entity(file: sourcePath, line: line, col: col, offset: offset), cascade: false)
}
else if let indexDB = project?.indexDB,
let entity = indexDB.declarationFor(filePath: sourcePath, line: line, col: col) {
setup(target: entity, cascade: false)
}
}
func filtered( _ lines: [String], _ entities: [Entity] ) -> String {
var path = entities[0].file, filename = relative( path )
filename = filename.substring(from: filename.range(of: "SDKs")?.lowerBound ?? filename.startIndex)
let linenos = Set( entities.map { $0.line } ).sorted(), body = linenos.map { lines[$0-1] }.joined()
return "<a class=sourceLink href=\"file:\(path)\">\(filename)</a>\n<div class='changesEntry'>\(body)</div>"
}
func processEntities(type: String) {
var changes = 0, files = 0
for entities in entitiesByFile.sorted( by: { $0[0].file < $1[0].file } ) {
let path = entities[0].file
if let sourceData = NSData(contentsOfFile: path) {
let lines = formatter.htmlFor(path: path, data: sourceData, entities: entities)
appendSource(title: path, text: filtered(lines, entities))
linecounts[path] = lines.count
changes += entities.count
files += 1
}
else {
xcode.log("Could not read \(path)")
}
}
appendSource(title: "", text: "\(changes) \(type) in \(files) file"+(files==1 ? "" : "s"))
}
@objc func depends( path: String ) {
setChangesSource(header: "Dependencies on \(relative(path))")
if let indexDB = project?.indexDB {
entitiesByFile = indexDB.dependsOn(path: path)
}
processEntities(type: "dependencies")
}
@objc func graphvizExport() {
open(url: formatter.gvfile)
}
func applySubstitution(oldValue: String, newValue: String) {
let newData = newValue.data(using: .utf8)!
let skew = newData.count - oldValue.utf8.count
setChangesSource(header: "Applying replacement of '\(oldValue)' with '\(newValue)'", isApply: true)
var modifications = 0
for entities in entitiesByFile.sorted( by: { $0[0].file < $1[0].file } ) {
let path = entities[0].file
if let contents = modified[path] ?? NSData(contentsOfFile: path) {
if originals[path] == nil {
originals[path] = contents
}
let out = NSMutableData()
var pos = 0, mods = 0
for entity in entities {
if let matches = entity.regex(text: oldValue).match(input: contents), Int(matches[2].rm_so) >= pos {
let startOffset = Int(matches[2].rm_so)
out.append( contents.subdata( with: NSMakeRange(pos, startOffset-pos) ) )
if let entity = history.last, path == entity.file && startOffset == entity.offset {
entity.offset = out.length
}
entity.offset = out.length
out.append( newData )
pos = Int(matches[2].rm_eo)
modifications += 1
mods += 1
}
else if wasSearch {
entity.notMatch = true
}
else {
xcode.log("Could not match \(newValue) at \(path)#\(entity.line):\(entity.col)")
}
}
out.append( contents.subdata( with: NSMakeRange(pos, contents.length-pos) ) )
modified[path] = out
let lines = formatter.htmlFor(path: path, data: out, entities: entities, skew: skew)
appendSource(title: path, text: filtered(lines, entities))
if lines.count != linecounts[path] {
xcode.log("Mismatched linecount \(lines.count) != \(linecounts[path]) for \(path)")
}
}
}
changes += "<div id=applying>Changing <span class=oldValue>'\(oldValue)'</span> to <span class=newValue>'\(newValue)'</span>...<div>"
appendSource(title: "", text: "\(modifications) modifications proposed")
}
func writeChanges( dict: [String:NSData], header: String ) {
setChangesSource(header: header, isApply: true)
for (path, data) in dict {
let wrote = data.write(toFile: path, atomically: true)
appendSource(title: path, text: "\(wrote ? "Wrote" : "Could not write to") \(path)\n")
}
let indexUpdateTime = 5.0
appendSource(title: "", text: "\nRefreshing in \(indexUpdateTime) seconds\n")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + indexUpdateTime) {
self.setup(target: self.history.last ?? self.defaultEntity())
}
formatter = Formatter()
modified.removeAll()
changes = ""
}
func searchProject(sender: AnyObject) {
let pattern = sender is NSMenuItem ? replacement.stringValue : findText.stringValue
setChangesSource(header: "USR search for pattern: \(pattern)")
guard let indexDB = project?.indexDB else {
xcode.error("Could load load index db \(project!.indexPath)")
return
}
replacement.stringValue = pattern
oldValue = pattern
wasSearch = true
entitiesByFile = indexDB.entitiesFor(pattern: pattern)
}
}
================================================
FILE: Refactorator/AppDelegate.swift
================================================
//
// AppDelegate.swift
// Refactorator
//
// Created by John Holdsworth on 19/11/2016.
// Copyright © 2016 John Holdsworth. All rights reserved.
//
// http://johnholdsworth.com/refactorator.html
//
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
class PathMenuItem: NSMenuItem {
let path: String
init(path: String, title: String, action: Selector) {
self.path = path
super.init(title: title, action: action, keyEquivalent: "")
}
required init(coder decoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class EntityMenuItem: NSMenuItem {
let entity: Entity
init(entity: Entity, title: String, action: Selector, keyEquivalent: String) {
self.entity = entity
super.init(title: title, action: action, keyEquivalent: keyEquivalent)
}
required init(coder decoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
@IBOutlet weak var window: NSWindow!
@IBOutlet var findPanel: NSPanel!
@IBOutlet weak var findText: NSTextField!
@IBOutlet weak var applyButton: NSButton!
@IBOutlet weak var saveButton: NSButton!
@IBOutlet weak var revertButton: NSButton!
@IBOutlet var state: AppController!
var saved = false
var licensed: Bool {
return UserDefaults.standard.string(forKey: colorKey) == myColor
}
@objc func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
NSApp.servicesProvider = self
NSUpdateDynamicServices()
state.history = NSDocumentController.shared().recentDocumentURLs.reversed().map { Entity( file: $0.path ) }
xcode = state
let defaults = UserDefaults.standard, countKey = "n"
let count = max(0,defaults.integer(forKey: countKey))+1
defaults.set(count, forKey: countKey)
// window.contentView?.wantsLayer = true
// window.appearance = NSAppearance(named: NSAppearanceNameVibrantDark)
_ = Integration(appDelegate: self, count: count)
}
@objc func application(_ theApplication: NSApplication, openFile filename: String ) -> Bool {
state.setup(target: Entity(file: filename))
return true
}
@objc func refactorator(_ pboard: NSPasteboard, userData: String, error: NSErrorPointer) {
_ = pboard.string(forType: NSPasteboardTypeString)
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(nil)
state.setup()
}
@objc func refactorFile(_ pboard: NSPasteboard, userData: String, error: NSErrorPointer) {
let options = [NSPasteboardURLReadingFileURLsOnlyKey:true]
if let fileURLs = pboard.readObjects(forClasses: [NSURL.self], options: options),
let file = (fileURLs.first as? NSURL)?.path {
state.setup(target: Entity(file: file))
}
}
@objc func windowWillClose(_ notification: Notification) {
// NSApp.hide(nil)
}
@objc func applicationDidUnhide(_ notification: Notification) {
// window.makeKeyAndOrderFront(nil)
}
@objc func applicationWillBecomeActive(_ notification: Notification) {
// window.makeKeyAndOrderFront(nil)
// state.setup()
}
@IBAction func syncToXcode(sender: NSMenuItem) {
state.setup()
}
@IBAction func open(sender: NSMenuItem) {
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.canChooseDirectories = false
panel.canCreateDirectories = false
panel.canChooseFiles = true
panel.begin { (result) -> Void in
if result == NSFileHandlingPanelOKButton,
let url = panel.url {
self.state.setup(target: Entity(file: url.path))
}
}
}
@IBAction func openInXcode(sender: NSMenuItem) {
if let current = state.history.last {
state.open(url: current.file)
}
}
@IBAction func openWorkspace(sender: NSMenuItem) {
if let workspace = state.project?.workspacePath {
state.open(url: workspace)
}
}
@IBAction func back(sender: NSMenuItem) {
if !state.history.isEmpty {
state.future.append( state.history.removeLast() )
let save = state.future
if !state.history.isEmpty {
let previous = state.history.removeLast()
state.setup(target: previous, cascade: false)
}
else {
let recentSources = NSDocumentController.shared().recentDocumentURLs
if recentSources.count > 1 {
state.setup(target: Entity(file: recentSources[1].path))
}
}
state.future = save
}
}
@IBAction func forward(sender: NSMenuItem) {
if !state.future.isEmpty {
state.setup(target: state.future.removeLast())
}
}
@IBAction func reload(sender: NSMenuItem) {
if let current = state.history.last {
state.setup(target: Entity(file: current.file))
}
}
@IBAction func printSource(sender: NSMenuItem) {
let pi = NSPrintInfo.shared()
pi.topMargin = 50
pi.leftMargin = 25
pi.rightMargin = 25
pi.bottomMargin = 50
pi.isHorizontallyCentered = false
NSPrintOperation(view: state.printWebView.mainFrame.frameView.documentView, printInfo: pi).run()
}
@IBAction func zapDerivedData(sender: NSMenuItem) {
let dir = HOME + "/Library/Developer/Xcode/DerivedData"
let alert = NSAlert()
alert.messageText = "Refactorator"
alert.informativeText = "This will \"rm -rf\" the contents of Xcode's DerivedData directory: \(dir). There are rare times when this can be a good idea."
alert.addButton(withTitle: "Cancel")
alert.addButton(withTitle: "I know what I'm doing")
if alert.runModal() == NSAlertSecondButtonReturn {
Process.run(path: "/bin/rm", args: ["-rf", dir])
}
}
@IBAction func undoChanges(sender: AnyObject) {
state.modified.removeAll()
state.changes = ""
}
@objc override func validateMenuItem(_ aMenuItem: NSMenuItem) -> Bool {
if let action = aMenuItem.action {
switch action {
case #selector(back(sender:)):
return !state.history.isEmpty
case #selector(forward(sender:)):
return !state.future.isEmpty
case #selector(openWorkspace(sender:)):
return state.project?.workspacePath != Project.unknown
case #selector(undoChanges(sender:)):
fallthrough
case #selector(saveChanges(sender:)):
return !state.modified.isEmpty
case #selector(revertSession(sender:)):
return !state.originals.isEmpty && saved
case #selector(buildProject(sender:)):
return state.project?.workspaceDoc != nil
case #selector(indexRebuild(sender:)):
return state.project?.projectRoot != Project.unknown
case #selector(buildSite(sender:)):
return state.project?.indexDB != nil
case #selector(navigateDirectories(sender:)):
if let navigateMenu = aMenuItem.submenu {
while navigateMenu.items.count != 0 {
navigateMenu.removeItem(at: 0)
}
if let indexDB = state.project?.indexDB {
var projectDirs = [String]()
for dirID in indexDB.projectDirIDs.keys {
if let wholePath = indexDB.directories[dirID] {
if wholePath.contains(".") {
continue
}
projectDirs.append( wholePath )
}
}
for wholePath in projectDirs.sorted() {
var dirPath = wholePath.url
var title = dirPath.lastPathComponent
while title == "Classes" || title == "Source" || title == "Pod" {
dirPath.deleteLastPathComponent()
title = dirPath.lastPathComponent
}
let item = PathMenuItem(path: wholePath, title: title,
action: #selector(navigateFiles(sender:)))
item.target = self
item.submenu = NSMenu()
// item.toolTip = wholePath
navigateMenu.addItem(item)
}
}
}
case #selector(navigateFiles(sender:)):
if let item = aMenuItem as? PathMenuItem,
let menu = item.submenu,
let indexDB = state.project?.indexDB {
for file in indexDB.files(inDirectory: item.path).sorted() {
let item = PathMenuItem(path: item.path+"/"+file, title: file,
action: #selector(navigateOpen(sender:)))
item.toolTip = item.path+"/"+file
item.target = self
menu.addItem(item)
}
}
case #selector(backEntities(sender:)):
if let backMenu = aMenuItem.submenu {
while backMenu.items.count != 0 {
backMenu.removeItem(at: 0)
}
var number = 0
for entity in state.history.reversed() {
if number > 0 {
let item = EntityMenuItem(entity: entity, title: entity.file.url.lastPathComponent,
action: #selector(backOpen(sender:)),
keyEquivalent: number == 1 ? "[" : "")
item.target = self
backMenu.addItem(item)
}
number += 1
}
}
default:
break
}
}
return true
}
@IBAction func applySubstitution(sender: NSButton) {
let newValue = state.replacement.stringValue
if newValue == myColor && !licensed {
state.setChangesSource(header: "<div class=licensed>You are now licensed. Thanks!</div><br><img src='data:image/png;base64,\(fireworks)'>", isApply: true)
UserDefaults.standard.set(newValue, forKey: colorKey)
UserDefaults.standard.synchronize()
return
}
if state.oldValue == "" {
state.setChangesSource(header: "Please select an entity before applying replacement", isApply: true)
return
}
state.applySubstitution(oldValue: state.oldValue, newValue: newValue)
state.oldValue = newValue
revertButton.isEnabled = !state.originals.isEmpty
saveButton.isEnabled = !state.modified.isEmpty
}
@IBAction func openFind(sender: NSMenuItem) {
if findText.stringValue == "" {
findText.stringValue = state.replacement.stringValue
}
findPanel.makeKeyAndOrderFront(nil)
}
@IBAction func findInSource(sender: AnyObject) {
let string = findText.stringValue
switch sender.tag {
case 1:
fallthrough
case 2:
state.sourceView.search( for: string, direction:true, caseSensitive:false, wrap:true)
case 3:
state.sourceView.search( for: string, direction:false, caseSensitive:false, wrap:true)
default:
break
}
}
@IBAction func searchProject(sender: AnyObject) {
state.searchProject(sender: sender)
state.processEntities(type: sender is AppDelegate ? "errors" : "references")
}
@IBAction func findOrphans(sender: NSMenuItem) {
state.setChangesSource(header: "Symbols declared but not referred to...")
guard let indexDB = state.project?.indexDB else {
xcode.error("Could load load index db \(state.project!.indexPath)")
return
}
state.entitiesByFile = indexDB.orphans()
state.processEntities(type: "unused symbols")
}
@IBAction func saveChanges(sender: AnyObject) {
state.writeChanges(dict: state.modified, header: state.changes)
saved = true
}
@IBAction func buildProject(sender: AnyObject) {
_ = state.project?.workspaceDoc?.build()
}
@IBAction func revertSession(sender: AnyObject) {
state.writeChanges(dict: state.originals, header: "Reverting changes...\n")
undoChanges(sender: self)
}
@IBAction func indexCheck(sender: NSMenuItem) {
findText.stringValue = "ERROR TYPE"
searchProject(sender: self)
}
@IBAction func searchSelection(sender: NSMenuItem) {
findText.stringValue = state.oldValue
searchProject(sender: self)
}
@IBAction func indexRebuild(sender: NSMenuItem) {
Process.run(path: "/usr/bin/find", args: [state.project!.projectRoot,
"-name", "*.swift", "-exec", "touch", "{}", ";"])
}
@IBAction func exportHTML(sender: NSMenuItem) {
let out = state.sourceView.windowScriptObject.evaluateWebScript("document.head.outerHTML + document.body.outerHTML") as! String
let panel = NSSavePanel()
panel.nameFieldStringValue = state.history.last!.file.url.deletingPathExtension().lastPathComponent+".html"
panel.begin { (result) -> Void in
if result == NSFileHandlingPanelOKButton,
let url = panel.url {
try? out.write(to: url, atomically: false, encoding: .utf8)
}
}
}
@IBAction func navigateDirectories(sender: AnyObject) {
}
@IBAction func navigateFiles(sender: AnyObject) {
}
@IBAction func navigateOpen(sender: PathMenuItem) {
state.setup(target: Entity(file: sender.path))
}
@IBAction func backEntities(sender: AnyObject) {
}
@IBAction func backOpen(sender: EntityMenuItem) {
state.history.last.flatMap { state.future.append($0) }
let save = state.future
state.setup(target: sender.entity)
state.future = save
}
@IBAction func buildSite(sender: AnyObject) {
guard let projectRoot = state.project?.projectRoot else { return }
state.formatter.buildSite( for: state.project!, into: projectRoot+"/html/", state: state)
}
@IBAction func findDependencies(sender: AnyObject) {
if sender as? NSMenuItem == state.dependsItem,
let path = state.history.last?.file {
state.depends(path: path)
}
else {
if !FileManager.default.fileExists(atPath: state.formatter.DOT_PATH) {
let alert = NSAlert()
alert.messageText = "Refactorator"
alert.informativeText = "Dependencies in your application " +
"can be displayed if you install \"dot\" from http://www.graphviz.org/."
alert.runModal()
state.open(url: "http://www.graphviz.org/Download_macos.php")
return
}
state.project = Project(target: state.history.last )
state.formatter.runDepends(state: state, standalone: false)
state.canvizView = nil
state.setup(target: state.canvizEntity)
}
}
@IBAction func findAssociated(sender: AnyObject) {
state.formatter.indexAssociations(filePath: state.history.last!.file, state: state)
}
}
let myIndex = {
() -> Int in
var index = 0
if var chars = getenv("USER") {
while chars.pointee != 0 {
index += Int(chars.pointee)
chars += 1
}
}
return index
}()
private let myColor = {
() -> String in
return colors[myIndex % colors.count]
}()
private let colorKey = "Color"
private var fireworks = "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"
================================================
FILE: Refactorator/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Refactorator/Base.lproj/MainMenu.xib
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="15G1212" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
<plugIn identifier="com.apple.WebKitIBPlugin" version="11762"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Injection" customModuleProvider="target">
<connections>
<outlet property="applyButton" destination="Izx-PW-zhY" id="iBw-bi-MwO"/>
<outlet property="findPanel" destination="A2I-eS-piB" id="f2N-7f-Gh0"/>
<outlet property="findText" destination="cpj-R8-vCt" id="rCd-e6-2rU"/>
<outlet property="revertButton" destination="IcZ-6U-OZY" id="pfI-XV-kvK"/>
<outlet property="saveButton" destination="atp-pA-4jR" id="0qy-Ju-HXL"/>
<outlet property="state" destination="nF1-TD-e5Y" id="dAj-Je-v3j"/>
<outlet property="window" destination="QvC-M9-y7g" id="jIt-wF-TW8"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="Refactorator" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Refactorator" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About Refactorator" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" hidden="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" hidden="YES" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide Refactorator" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit Refactorator" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="New" hidden="YES" keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
</connections>
</menuItem>
<menuItem title="Back" keyEquivalent="[" toolTip="Navigate to previous file" id="SvX-iU-EQT">
<connections>
<action selector="backWithSender:" target="Voe-Tx-rLC" id="lfQ-jG-uDH"/>
</connections>
</menuItem>
<menuItem title="Forward" keyEquivalent="]" toolTip="Navigate to previous file" id="SgJ-iz-IgR">
<connections>
<action selector="forwardWithSender:" target="Voe-Tx-rLC" id="6eK-ju-wUY"/>
</connections>
</menuItem>
<menuItem title="Navigate" id="Ks8-6s-W0O">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Navigate" id="QcH-1k-rC4"/>
<connections>
<action selector="navigateDirectoriesWithSender:" target="Voe-Tx-rLC" id="eeo-9U-3py"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Open…" keyEquivalent="o" id="ss4-gS-ptz">
<connections>
<action selector="openWithSender:" target="Voe-Tx-rLC" id="Ecz-sM-83p"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Open in Xcode" keyEquivalent="O" toolTip="Open Source file in default editor" id="ABn-uE-BS1">
<connections>
<action selector="openInXcodeWithSender:" target="Voe-Tx-rLC" id="Zgk-9P-ugI"/>
</connections>
</menuItem>
<menuItem title="Open Project" keyEquivalent="o" toolTip="Open associated Xcode project" id="alq-4I-Ur0">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="openWorkspaceWithSender:" target="Voe-Tx-rLC" id="gAo-vk-GDx"/>
</connections>
</menuItem>
<menuItem title="Sync to Xcode" keyEquivalent="y" id="IAo-SY-fd9">
<connections>
<action selector="syncToXcodeWithSender:" target="Voe-Tx-rLC" id="pZd-Bk-QaN"/>
</connections>
</menuItem>
<menuItem title="Undo Apply" keyEquivalent="u" toolTip="Undo any changes applied in memory" id="pxx-59-PXV">
<connections>
<action selector="undoChangesWithSender:" target="Voe-Tx-rLC" id="Hvl-b3-J5l"/>
</connections>
</menuItem>
<menuItem title="Save To Disk…" keyEquivalent="s" toolTip="Save all modifications to disk" id="GfA-ng-gpe">
<connections>
<action selector="saveChangesWithSender:" target="Voe-Tx-rLC" id="7Eg-1X-K8S"/>
</connections>
</menuItem>
<menuItem title="Perform Build…" keyEquivalent="b" toolTip="Perform a test build of the project" id="Bw7-FT-i3A">
<connections>
<action selector="buildProjectWithSender:" target="Voe-Tx-rLC" id="MW0-0S-R9p"/>
</connections>
</menuItem>
<menuItem title="Revert Changes" keyEquivalent="r" toolTip="Revert all changes made since Refactorator launched" id="KaW-ft-85H">
<connections>
<action selector="revertSessionWithSender:" target="Voe-Tx-rLC" id="RHO-L8-b6g"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Zap DervivedData" hidden="YES" toolTip="Completely remove Xcode's DrivedDat directory to force index rebuild." id="tVJ-u1-yHX">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="zapDerivedDataWithSender:" target="Voe-Tx-rLC" id="D0J-Iu-pjm"/>
</connections>
</menuItem>
<menuItem title="Check Index DB" toolTip="Check index database for obvious errors" id="DA7-dj-Vke">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="indexCheckWithSender:" target="Voe-Tx-rLC" id="R3f-9d-yWp"/>
</connections>
</menuItem>
<menuItem title="Force Re-Index" toolTip="Touch all swift files to force index re-build" id="0pR-Gj-uH2">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="indexRebuildWithSender:" target="Voe-Tx-rLC" id="wZt-mp-h0F"/>
</connections>
</menuItem>
<menuItem title="Index Protocols" toolTip="Re-index project to find associateion bwteen USRs e.g. implements protocol" id="duR-AO-ekK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="findAssociatedWithSender:" target="Voe-Tx-rLC" id="qu1-vh-8Y2"/>
</connections>
</menuItem>
<menuItem title="Dependencies" toolTip="Find dependencies bewteen source files (requires Graphviz)" id="AK8-sM-fgc">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="findDependenciesWithSender:" target="Voe-Tx-rLC" id="nQo-Tc-GRe"/>
</connections>
</menuItem>
<menuItem title="Export HTML" toolTip="Extract olourised HTML for source" id="xV4-lN-mus">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="exportHTMLWithSender:" target="Voe-Tx-rLC" id="QJo-IQ-Zb6"/>
</connections>
</menuItem>
<menuItem title="Build Site" toolTip="Build cros linked HTML site for your project" id="x9U-ND-vGe">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="buildSiteWithSender:" target="Voe-Tx-rLC" id="bHd-fG-xE2"/>
</connections>
</menuItem>
<menuItem title="Close" keyEquivalent="w" toolTip="Close window and exit" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="-1" id="ehy-yF-tr1"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="RZQ-bj-ZDO"/>
<menuItem title="Page Setup…" hidden="YES" keyEquivalent="P" id="qIS-W8-SiK">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
<connections>
<action selector="printSourceWithSender:" target="Voe-Tx-rLC" id="J2e-tC-nGG"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" hidden="YES" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" hidden="YES" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" hidden="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" hidden="YES" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" hidden="YES" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" hidden="YES" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Spelling and Grammar" hidden="YES" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" hidden="YES" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" hidden="YES" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" hidden="YES" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" toolTip="Search in source of project" id="Xz5-n4-O0W">
<connections>
<action selector="openFindWithSender:" target="Voe-Tx-rLC" id="9hX-CZ-m98"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" hidden="YES" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" toolTip="Find next" id="q09-fT-Sye">
<connections>
<action selector="findInSourceWithSender:" target="Voe-Tx-rLC" id="2ac-Pf-Dnc"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" toolTip="Find previous" id="OwM-mh-QMV">
<connections>
<action selector="findInSourceWithSender:" target="Voe-Tx-rLC" id="nG6-aF-4cY"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" hidden="YES" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Project Search" keyEquivalent="g" toolTip="grep through project Symbols using a regex" id="gbP-jh-pGT">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="openFindWithSender:" target="Voe-Tx-rLC" id="k7k-IT-vf2"/>
</connections>
</menuItem>
<menuItem title="Find Unused" toolTip="Search for symbols declared but not referred to in code" id="jEQ-l8-b1a">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="findOrphansWithSender:" target="Voe-Tx-rLC" id="WaZ-nh-8RO"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" hidden="YES" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" hidden="YES" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="-1" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" hidden="YES" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleSourceList:" target="-1" id="iwa-gc-5KM"/>
</connections>
</menuItem>
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="General Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="helpWithSender:" target="Voe-Tx-rLC" id="sYW-Kc-obu"/>
</connections>
</menuItem>
<menuItem title="Shout John a Beer" keyEquivalent="🍺" id="yLP-Wv-ElC">
<connections>
<action selector="donateWithSender:" target="Voe-Tx-rLC" id="eGG-0t-KC3"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<window title="Welcome to Refactorator" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="Refactorator" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="718" height="713"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1177"/>
<view key="contentView" wantsLayer="YES" misplaced="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="718" height="713"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<splitView misplaced="YES" arrangesAllSubviews="NO" dividerStyle="paneSplitter" id="R7d-nT-bCd">
<rect key="frame" x="0.0" y="0.0" width="718" height="713"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<customView id="D5a-sP-aUT">
<rect key="frame" x="0.0" y="0.0" width="718" height="484"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<webView misplaced="YES" id="hoP-PD-VYj">
<rect key="frame" x="0.0" y="38" width="718" height="446"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<webPreferences key="preferences" defaultFontSize="12" defaultFixedFontSize="12" plugInsEnabled="NO" javaEnabled="NO" javaScriptCanOpenWindowsAutomatically="NO" allowsAnimatedImages="NO" allowsAnimatedImageLooping="NO">
<nil key="identifier"/>
</webPreferences>
</webView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" id="5nb-3q-Dw7">
<rect key="frame" x="9" y="11" width="73" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Rename to:" id="cs8-CC-T22">
<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 toolTip="New value for idedntifier name" verticalHuggingPriority="750" misplaced="YES" id="hvb-Q7-aD4">
<rect key="frame" x="85" y="8" width="287" height="22"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="Pgk-cL-dwp">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<action selector="performClick:" target="Izx-PW-zhY" id="TuX-VO-6R9"/>
</connections>
</textField>
<button toolTip="Apply change in memory" verticalHuggingPriority="750" misplaced="YES" id="Izx-PW-zhY">
<rect key="frame" x="374" y="2" width="83" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="push" title="Apply" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="nsv-rB-oCY">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="applySubstitutionWithSender:" target="Voe-Tx-rLC" id="iyT-6H-3yF"/>
</connections>
</button>
<button toolTip="Save changes to disk" verticalHuggingPriority="750" misplaced="YES" id="atp-pA-4jR">
<rect key="frame" x="452" y="2" width="81" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="push" title="Save" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="sqZ-NX-jMo">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="saveChangesWithSender:" target="Voe-Tx-rLC" id="8Tc-ZS-Y6D"/>
</connections>
</button>
<button toolTip="Check that project still builds" verticalHuggingPriority="750" misplaced="YES" id="BoD-Ix-wka">
<rect key="frame" x="527" y="2" width="113" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="push" title="Check Build" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="10p-49-7Ni">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="buildProjectWithSender:" target="Voe-Tx-rLC" id="Bp1-Kr-bfO"/>
</connections>
</button>
<button toolTip="Revert all changes since the beginning of the session" verticalHuggingPriority="750" misplaced="YES" id="IcZ-6U-OZY">
<rect key="frame" x="633" y="2" width="80" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="push" title="Revert" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="F0V-iV-ebh">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="revertSessionWithSender:" target="Voe-Tx-rLC" id="kV2-z5-yUJ"/>
</connections>
</button>
</subviews>
</customView>
<customView id="a6X-bH-sPj">
<rect key="frame" x="0.0" y="494" width="718" height="219"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<webView misplaced="YES" maintainsBackForwardList="NO" id="Kfe-Ht-0tr">
<rect key="frame" x="0.0" y="0.0" width="718" height="219"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<webPreferences key="preferences" defaultFontSize="12" defaultFixedFontSize="12" plugInsEnabled="NO" javaEnabled="NO" javaScriptCanOpenWindowsAutomatically="NO" allowsAnimatedImages="NO" allowsAnimatedImageLooping="NO">
<nil key="identifier"/>
</webPreferences>
</webView>
</subviews>
</customView>
</subviews>
<holdingPriorities>
<real value="250"/>
<real value="250"/>
</holdingPriorities>
</splitView>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="Kiw-DV-A2f"/>
</connections>
<point key="canvasLocation" x="-230" y="-291.5"/>
</window>
<menuItem title="Reload" id="zJ0-Jr-CmZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reloadWithSender:" target="Voe-Tx-rLC" id="lwD-b1-IGA"/>
</connections>
</menuItem>
<window title="Find" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="A2I-eS-piB" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" utility="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="132" width="304" height="42"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1177"/>
<view key="contentView" misplaced="YES" id="VUZ-zJ-xWR">
<rect key="frame" x="0.0" y="0.0" width="304" height="42"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField verticalHuggingPriority="750" misplaced="YES" id="cpj-R8-vCt">
<rect key="frame" x="46" y="10" width="96" height="22"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="2V0-ke-led">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<action selector="performClick:" target="qz6-LG-DWp" id="M27-D9-nWM"/>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" id="Thy-91-Pbc">
<rect key="frame" x="8" y="13" width="34" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Find:" id="iVO-hr-Fxk">
<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>
<button toolTip="Find next" verticalHuggingPriority="750" misplaced="YES" tag="2" id="qz6-LG-DWp">
<rect key="frame" x="143" y="4" width="83" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="In File.." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="VTy-fC-trt">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="findInSourceWithSender:" target="Voe-Tx-rLC" id="UYi-Sb-cld"/>
</connections>
</button>
<button toolTip="grep through project Symbols using a regex" verticalHuggingPriority="750" misplaced="YES" id="fa0-Zh-DT5">
<rect key="frame" x="219" y="4" width="83" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Project" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="rp5-Z9-4T0">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="searchProjectWithSender:" target="Voe-Tx-rLC" id="oz2-BR-lIG"/>
</connections>
</button>
</subviews>
</view>
<point key="canvasLocation" x="-142" y="246"/>
</window>
<menuItem title="Forward" keyEquivalent="]" id="rZ9-vB-ZMx">
<connections>
<action selector="forwardWithSender:" target="Voe-Tx-rLC" id="DUY-3P-KZX"/>
</connections>
</menuItem>
<menuItem title="Search USRs" toolTip="Search project for USRs matching selection" id="kWr-TP-7Pz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="searchSelectionWithSender:" target="Voe-Tx-rLC" id="ZFB-Uh-Dt1"/>
</connections>
</menuItem>
<menuItem title="Search USRs" toolTip="Search project for USRs matching selection" id="iD6-4l-Ufq">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="searchSelectionWithSender:" target="Voe-Tx-rLC" id="gqS-Mf-gXB"/>
</connections>
</menuItem>
<menuItem title="Dependencies" toolTip="Search project for USRs matching selection" id="T6k-0J-mIM" userLabel="Dependencies">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="findDependenciesWithSender:" target="Voe-Tx-rLC" id="bxb-5V-J0Q"/>
</connections>
</menuItem>
<customObject id="nF1-TD-e5Y" customClass="AppController" customModule="Injection" customModuleProvider="target">
<connections>
<outlet property="backItem" destination="tq0-Xd-Lwk" id="bGs-XU-SEQ"/>
<outlet property="changesView" destination="Kfe-Ht-0tr" id="Pci-C5-kAq"/>
<outlet property="dependsItem" destination="T6k-0J-mIM" id="paB-69-x0Y"/>
<outlet property="findText" destination="cpj-R8-vCt" id="aFk-nw-QAW"/>
<outlet property="forwardItem" destination="rZ9-vB-ZMx" id="HsK-1X-VsG"/>
<outlet property="reloadItem" destination="zJ0-Jr-CmZ" id="8Gs-yO-6CK"/>
<outlet property="replacement" destination="hvb-Q7-aD4" id="2cv-HW-epU"/>
<outlet property="searchItem" destination="kWr-TP-7Pz" id="4c1-Zd-Slh"/>
<outlet property="sourceView" destination="hoP-PD-VYj" id="aCi-kN-Hfe"/>
<outlet property="window" destination="QvC-M9-y7g" id="lsx-29-EL8"/>
</connections>
</customObject>
<menuItem title="Back" id="tq0-Xd-Lwk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Back" id="14n-z9-aqj"/>
<connections>
<action selector="backEntitiesWithSender:" target="Voe-Tx-rLC" id="ntF-lD-c1N"/>
</connections>
</menuItem>
</objects>
</document>
================================================
FILE: Refactorator/Common.swift
================================================
//
// Common.swift
// Refactorator
//
// Created by John Holdsworth on 18/12/2016.
// Copyright © 2016 John Holdsworth. All rights reserved.
//
import Foundation
var xcode: AppLogging!
protocol AppLogging {
func log( _ msg: String )
func error( _ msg: String )
}
func htmlEscape( _ str: String? ) -> String {
if let str = str {
return str.contains("<") || str.contains("&") ?
str.replacingOccurrences(of: "&", with: "&")
.replacingOccurrences(of: "<", with: "<") : str
}
return "nil"
}
extension Process {
@discardableResult
class func run(path: String, args: [String]) -> Int32 {
let task = Process()
task.launchPath = path
task.arguments = args
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
}
extension DispatchGroup {
class func inParallel<T>( work: [T], workers: Int = 4,
processor: @escaping (T, @escaping (() -> Void) -> Void) -> Void ) {
let threadPool = DispatchGroup()
let lock = NSLock()
func locked<L>( block: () -> L ) -> L {
lock.lock()
let ret = block()
lock.unlock()
return ret
}
var work = work
for _ in 0..<workers {
threadPool.enter()
DispatchQueue.global().async {
while let next = locked( block: {
() -> T? in
return !work.isEmpty ? work.removeFirst() : nil
} ) {
processor( next, locked )
}
threadPool.leave()
}
}
threadPool.wait()
}
}
================================================
FILE: Refactorator/Coverage.swift
================================================
//
// Coverage.swift
// Refactorator
//
// Created by John Holdsworth on 03/03/2017.
// Copyright © 2017 John Holdsworth. All rights reserved.
//
import Foundation
class Coverage {
let covered: Set<Int>
init?( file: String, project: Project ) {
let coverageFile = project.derivedData.url
.appendingPathComponent("Build/Intermediates/CodeCoverage/Coverage.profdata").path
if !FileManager.default.fileExists(atPath: coverageFile) {
return nil
}
let task = Process()
task.launchPath = Bundle.main.path(forResource: "coverage", ofType: ".rb")
task.arguments = [coverageFile, file]
let output = Pipe()
task.standardOutput = output.fileHandleForWriting
task.launch()
output.fileHandleForWriting.closeFile()
let data = output.fileHandleForReading.readDataToEndOfFile()
task.waitUntilExit()
if let coverage = String(data: data, encoding: .utf8) {
covered = Set<Int>( coverage.components(separatedBy: "\n").map { Int($0, radix:10) ?? -1 } )
}
else {
return nil
}
}
}
================================================
FILE: Refactorator/Credits.rtf
================================================
{\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470
{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 LucidaGrande;\f2\fnil\fcharset0 Menlo-Regular;
\f3\fnil\fcharset0 HelveticaNeue;\f4\fmodern\fcharset0 Courier;}
{\colortbl;\red255\green255\blue255;\red83\green98\blue108;}
\paperw11900\paperh16840\vieww9600\viewh8400\viewkind0
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\partightenfactor0
\f0\fs24 \cf0 Refactorator is a user interface to an Xcode project's SQLite index database that allows you to rename identifiers that have a "Unified Symbol Reference" i.e. types, variables, ivars, functions & enum cases. For local variables use "Edit All in Scope". \
\
Whenever you launch Refactorator it communicates with Xcode through it's Apple Script API (see Xcode.h in the app bundle) to determine the current source file, topmost workspace and selection. It also uses this to locate the index database in "DerivedData".\
\
To rename the selected symbol, enter a new name into the text field in the middle of the display and click the "
\b Apply
\b0 " button or press enter. If the changes look OK, use the "
\b Save
\b0 " button to save them to disk then click the the "
\b Check Build
\b0 " button to make sure your project still builds.\
\
If the project no longer builds use the "
\b Revert
\b0 " button to undo all changes in the current session.\
\
Refactorator is designed to be used as a macOS service which is installed when you first run the application. To enable it go to "
\b Keyboard/Shortcuts/Services
\b0 " in the System Preferences app and enable the "Refactorator" and "Refactor File" services. After this you should be able to refactor from Xcode by selecting an identifier and typing
\f1 \uc0\u8984
\f0 -~ (cmd-tilde) or by using the right-click context menu under "Services" or direct from Finder.\
\
It can also be used as a source browser. Clicking on a symbol in top half of the screen will list the places it is used in the application while clicking on a symbol in the bottom half of the screen will load that source into the source viewer.
\f1 \uc0\u8984
\f0 clicking should take you to the definition. There are a couple of other features where you can search a project for specific text in a symbol USR or locate all symbols declared but not referenced in the code. \
\
Refactorator's ability to correctly find all occurrences of a symbol in a program is dependent on the quality of the index db which could be described as "generally complete" but it does sometimes get out of sync and you may have to manually fix errors at times. If you're having trouble, rebuild the project, test the project and then use the menu item "File/Force Re-index" to touch all Swift files in your project to force a complete re-index.\
\
If you're not keen how Refactorator looks, change the CSS styling in the file "Source.html" in the the app bundle. Feel free to send me an update!\
\
For further help go to {\field{\*\fldinst{HYPERLINK "http://johnholdsworth.com/refactorator.html"}}{\fldrslt http://johnholdsworth.com/refactorator.html}}\
\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0
\f2\fs22 \cf0 Copyright (C) 2016 John Holdsworth {\field{\*\fldinst{HYPERLINK "mailto:refactorator@johnholdsworth.com"}}{\fldrslt refactorator@johnholdsworth.com}} {\field{\*\fldinst{HYPERLINK "https://twitter.com/Injection4Xcode"}}{\fldrslt
\f3\fs24 \cf2 \expnd0\expndtw0\kerning0
@Injection4Xcode}}\
\
\pard\pardeftab720\partightenfactor0
\f4\fs24 \cf0 \expnd0\expndtw0\kerning0
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated \
documentation files (the "Software"), to deal in the Software without restriction, including without limitation \
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, \
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\
\
The above copyright notice and this permission notice shall be included in all copies or substantial \
portions of the Software.\
\
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT \
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. \
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE \
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0
\f2\fs22 \cf0 \kerning1\expnd0\expndtw0 .\
}
================================================
FILE: Refactorator/Formatter.swift
================================================
//
// Formatter.swift
// Refactorator
//
// Created by John Holdsworth on 13/12/2016.
// Copyright © 2016 John Holdsworth. All rights reserved.
//
import Foundation
import WebKit
protocol AppGui: AppLogging {
var project: Project? { get }
func sourceHTML() -> String
func setChangesSource( header: String?, target: Entity?, isApply: Bool )
func appendSource( title: String, text: String )
func relative( _ path: String ) -> String
func open( url: String )
}
class Formatter {
static let sourceKit = SourceKit()
var maps = [String:(mtime: TimeInterval, resp: sourcekitd_response_t)]()
let newline = CChar("\n".utf16.last!)
func htmlFor( path: String, data: NSData, entities: [Entity]? = nil, skew: Int = 0, selecting: Entity? = nil, cascade: Bool = true, shortform: Bool = false, cleanPath: String? = nil, coverage: Set<Int>? = nil,
linker: @escaping (_ text: String, _ entity: Entity?) -> String = {
(_ text: String, _ entity: Entity?) -> String in
return text
} ) -> [String] {
var ptr = 0, skewtotal = 0, line = 1, col = 1, entityNumber = 0, html = ""
let sourceBytes = data.bytes.assumingMemoryBound(to: CChar.self)
func skipTo( offset: Int ) -> (text: String, entity: Entity?) {
if offset <= ptr {
return ("", nil)
}
let selectedByOffset = ptr == selecting?.offset
var offset = offset + skewtotal
var currentEntity: Entity?
if let entities = entities, entityNumber < entities.count {
var entity = entities[entityNumber]
while (entity.line < line || entity.line == line && entity.col < col) && entityNumber + 1 < entities.count {
// xcode.log("Missed entity \(entity.line):\(entity.col) < \(line):\(col) \(entity.usr) - \(path)")
entityNumber += 1
entity = entities[entityNumber]
}
if entity.line == line && entity.col == col || entity.offset == ptr+skewtotal {
// xcode.log("entity \(entity.line):\(entity.col) == \(line):\(col) \(entity.usr) - \(path)")
currentEntity = entity
entityNumber += 1
if !entity.notMatch {
offset += skew
}
}
}
let out = NSString( bytes: sourceBytes+ptr+skewtotal,
length: offset-(ptr+skewtotal),
encoding: String.Encoding.utf8.rawValue ) ??
NSString( bytes: sourceBytes+ptr+skewtotal,
length: offset-(ptr+skewtotal),
encoding: String.Encoding.isoLatin1.rawValue ) ??
"?\(ptr+skewtotal)?\(offset-(ptr+skewtotal))?" as NSString
while ptr+skewtotal < offset {
if sourceBytes[ptr+skewtotal] == newline {
line += 1
col = 1
}
else {
col += 1
}
ptr += 1
}
if currentEntity?.notMatch == false {
skewtotal += skew
ptr -= skew
}
// ptr = offset-skews
var escaped = htmlEscape( out as String )
if currentEntity?.decl == true {
escaped = "<span class=declaration>\(escaped)</span>"
}
if selectedByOffset || currentEntity != nil && (currentEntity == selecting || selecting == nil) {
escaped = "<span class=highlighted id=selected cascade=\(cascade ? 1 : 0)>\(escaped)</span>"
}
return (escaped, currentEntity)
}
isTTY = false
let sourceKit = Formatter.sourceKit
let cleanPath = cleanPath ?? path
let modified = mtime( path )
let resp = (modified == maps[path]?.mtime ? maps[path]?.resp : nil) ?? sourceKit.syntaxMap(filePath: path)
maps[path] = (modified,resp)
var extensions = [String:String]()
let dict = SKApi.sourcekitd_response_get_value( resp )
let map = SKApi.sourcekitd_variant_dictionary_get_value( dict, sourceKit.syntaxID )
_ = SKApi.sourcekitd_variant_array_apply( map ) { (_,dict) in
let kind = dict.getUUIDString( key: sourceKit.kindID )
let offset = dict.getInt( key: sourceKit.offsetID )
let length = dict.getInt( key: sourceKit.lengthID )
let kindSuffix = extensions[kind] ?? kind.url.pathExtension
if extensions[kind] == nil {
extensions[kind] = kindSuffix
}
html += skipTo( offset: offset ).text
var span = "<span"
if !shortform {
span += " line=\(line) col=\(col) offset=\(ptr) title=\"\(cleanPath)#\(line):\(col)"
}
var (text, entity) = skipTo( offset: offset+length )
let type = entity != nil ? "Xc\(entity!.kindSuffix) " : ""
let usr = entity?.usr != nil ? htmlEscape( demangle( entity!.usr! ) ) : ""
if !shortform {
span += " \(usr) \(entity?.kind ?? "") \(entity?.role ?? -1)\""
}
span += " class='\(type)\(kindSuffix)' entity=\(entity != nil || entities == nil ? 1 : 0)>"
if kindSuffix == "url" {
text = "<a href=\"\(text)\">\(text)</a>"
}
html += "\(span)\(linker(text, entity))</span>"
return true
}
html += skipTo( offset: data.length-skewtotal ).0
var lineno = 0
let lines = html.components(separatedBy: "\n").map {
(line) -> String in
lineno += 1
var classes = "linenumber"
if coverage?.contains(lineno) == true {
classes += " covered"
}
return String(format:"<span class='\(classes)' id=L\(lineno)>%04d </span>", lineno)+line+"\n"
}
return lines
}
func htmlFile(_ state: AppGui, _ path: String ) -> String {
return state.relative( path ).replacingOccurrences(of: "/", with: "_") + ".html"
}
func buildSite( for project: Project, into htmlDir: String, state: AppGui ) {
state.setChangesSource(header: "Building source site into \(htmlDir)", target: nil, isApply: false)
try? FileManager.default.createDirectory(atPath: htmlDir, withIntermediateDirectories: false, attributes: nil)
if var entiesForFiles = project.indexDB?.projectEntities() {
var referencesByUSR = [Int:[Entity]]()
var declarationsByUSR = [Int:Entity]()
var dataByFile = [String:NSData]()
var linesByFile = [String:[String]]()
for entities in entiesForFiles {
for entity in entities {
if let usrID = entity.usrID {
if referencesByUSR[usrID] == nil {
referencesByUSR[usrID] = [Entity]()
}
referencesByUSR[usrID]!.append(entity)
if entity.decl {
declarationsByUSR[usrID] = entity
}
}
}
}
DispatchGroup.inParallel(work: entiesForFiles) {
(entities, locked) in
let path = entities[0].file
let data = NSData(contentsOfFile: path) ?? NSData()
let html = self.htmlFor(path: path, data: data, entities: entities, shortform: true)
locked {
dataByFile[path] = data
linesByFile[path] = html
}
}
for (usrID, _) in referencesByUSR {
referencesByUSR[usrID]!.sort { $0.0 < $0.1 }
}
func href( _ entity: Entity ) -> String {
return "\(htmlFile(state, entity.file))#L\(entity.line)"
}
let common = state.sourceHTML()
DispatchGroup.inParallel(work: entiesForFiles) {
(entities, locked) in
let path = entities[0].file
let out = common + self.htmlFor(path: path, data: dataByFile[path]!, entities: entities, selecting: Entity(file:""), cleanPath: state.relative(path) ) {
(text, entity) -> String in
var text = text
if let entity = entity,
let decl = declarationsByUSR[entity.usrID!],
let related = referencesByUSR[entity.usrID!] {
if related.count > 1 {
if entity.decl || state.project?.indexDB?.podDirIDs[entity.dirID] == nil {
var popup = ""
for ref in related {
if ref == entity {
continue
}
let keepListOpen = ref.file != decl.file ? "event.stopPropagation(); " : ""
popup += "<tr\(ref == decl ? " class=decl" : "")><td style='text-decoration: underline;' " +
"onclick='document.location.href=\"\(href(ref))\"; \(keepListOpen)return false;'>\(ref.file.url.lastPathComponent)</td>"
let lines = linesByFile[ref.file] ?? []
let reference = ref.line < lines.count ? lines[ref.line-1] : ""
popup += "<td><pre>\(reference.replacingOccurrences(of: "\n", with: ""))</pre></td>"
}
text = "<a style='color: inherit' name='L\(entity.line)' href='\(href(decl))' target=_self onclick='return expand(this, event.metaKey);'>" +
"\(text)<span class='references'><table>\(popup)</table></span></a>"
}
else {
text = "<a style='color: inherit' href='\(href(decl))'>\(text)</a>"
}
}
else if entity.decl == true {
text = "<a style='color: inherit' name='L\(entity.line)' no_href='\(href(decl))'>\(text)</a>"
}
}
return text
}.joined()
let final = htmlDir.url.appendingPathComponent(self.htmlFile(state, path))
do {
try out.write(to: final, atomically: false, encoding: .utf8)
DispatchQueue.main.async {
state.appendSource(title: "", text: "Wrote <a href=\"file://\(final.path)\">\(final.path)</a>\n")
}
}
catch (let e) {
print("Could not save to \(final): \(e)")
}
}
do {
let workspace = state.project?.workspaceName ?? "unknown"
var sources = common+"</pre><div class=filelist><h2>Sources for Project \(workspace)</h2><script> document.title = 'Sources for Project \(workspace)' </script>"
for entities in entiesForFiles.sorted(by: { $0.0[0].file < $0.1[0].file }) {
let path = entities[0].file
sources += "<a href='\(htmlFile(state, path))'>\(state.relative(path))</a><br>"
}
sources += "<p>Cross Reference can be found <a href='xref.html'>here</a>."
sources += "<p>Dependencies Graph can be found <a href='canviz.html'>here</a>."
let index = htmlDir+"index.html"
try sources.write(toFile: index, atomically: false, encoding: .utf8)
DispatchQueue.main.async {
state.appendSource(title: "", text: "Wrote <a href=\"file://\(index)\">\(index)</a>\n")
}
sources = common+"</pre><div class=filelist><h2>Symbols defined in \(workspace)</h2><script> document.title = 'Symbols defined in \(workspace)' </script>"
for entity in declarationsByUSR.values.sorted(by: {demangle($0.0.usr)! < demangle($0.1.usr)!}) {
sources += "<a href='\(href(entity))'>\(htmlEscape( demangle(entity.usr) ))</a><br>"
}
let xref = htmlDir+"xref.html"
try sources.write(toFile: xref, atomically: false, encoding: .utf8)
DispatchQueue.main.async {
state.appendSource(title: "", text: "Wrote <a href=\"file://\(xref)\">\(xref)</a>\n")
}
state.open(url: index)
runDepends(state: state, standalone: true)
let dotFile = htmlDir+"refactorator.gv"
try? FileManager.default.removeItem(atPath: dotFile)
try FileManager.default.copyItem(atPath: gvfile, toPath: dotFile)
let canviz = Bundle.main.path(forResource: "canviz-0.1", ofType: nil)!
try? FileManager.default.copyItem(atPath: canviz, toPath: htmlDir+"canviz-0.1")
let canviz2 = Bundle.main.path(forResource: "canviz2", ofType: "html")!
try? FileManager.default.removeItem(atPath: htmlDir+"canviz.html")
try FileManager.default.copyItem(atPath: canviz2, toPath: htmlDir+"canviz.html")
}
catch (let e) {
state.error( "Error building site: \(e)")
}
}
}
var DOT_PATH = "/usr/local/bin/dot"
var gvfile: String {
return "/tmp/canviz.gv"
}
func runDepends( state: AppGui, standalone: Bool ) {
var nodeID = 0, nodes = [String:Int]()
var dot = "digraph xref {\n node [fontname=\"Arial\"];\n"
func defineNode( _ path: String ) -> String {
if nodes[path] == nil {
nodeID += 1
nodes[path] = nodeID
let label = path.hasSuffix(".swift") ?
path.url.deletingPathExtension().lastPathComponent : path.url.lastPathComponent
dot += "N\(nodeID) [href=\"javascript:void(click_node('\(standalone ? htmlFile(state, path) : path)'))\" " +
"label=\"\(label)\" tooltip=\"\(state.relative(path))\"];\n"
}
return "N\(nodes[path]!)"
}
for (to, from, count) in state.project!.indexDB!.dependencies() {
dot += " \(defineNode( from )) -> \(defineNode( to )) [penwidth=\(log10(Double(count)))]\n"
}
dot += "}\n"
let dotfile = "/tmp/refactorator.dot"
try? dot.write(toFile: dotfile, atomically: false, encoding: .utf8)
Process.run(path: DOT_PATH, args: [dotfile, "-Txdot", "-o"+gvfile])
}
func indexAssociations( filePath: String, state: AppGui ) {
let logDir = state.project!.derivedData+"/Logs/Build"
let xcodeBuildLogs = LogParser( logDir: logDir )
guard let argv = xcodeBuildLogs.compilerArgumentsMatching( matcher: { line in
line.contains( " -primary-file \(filePath) " ) ||
line.contains( " -primary-file \"\(filePath)\" " ) } ) else {
xcode.error( "Could not find compiler arguments in \(logDir). Have you built all files in the project?" )
return
}
state.setChangesSource(header: "Re-indexing to capture associations between USRs", target: nil, isApply: false)
gitextract_cm_vpaff/
├── .gitignore
├── LICENSE
├── README.md
├── Refactorator/
│ ├── App.icns
│ ├── AppController.swift
│ ├── AppDelegate.swift
│ ├── Assets.xcassets/
│ │ └── AppIcon.appiconset/
│ │ └── Contents.json
│ ├── Base.lproj/
│ │ └── MainMenu.xib
│ ├── Common.swift
│ ├── Coverage.swift
│ ├── Credits.rtf
│ ├── Formatter.swift
│ ├── Info.plist
│ ├── Integration.swift
│ ├── Intro.html
│ ├── Project.swift
│ ├── Refactorator-Bridging-Header.h
│ ├── Source.html
│ ├── Utils.h
│ ├── Utils.m
│ ├── Xcode.h
│ ├── canviz.html
│ ├── canviz2.html
│ └── coverage.rb
├── Refactorator.xcodeproj/
│ ├── project.pbxproj
│ └── project.xcworkspace/
│ └── contents.xcworkspacedata
└── canviz-0.1/
├── LICENSE.txt
├── README.txt
├── canviz.css
├── canviz.js
├── path/
│ └── path.js
└── prototype/
└── prototype.js
SYMBOL INDEX (28 symbols across 4 files)
FILE: Refactorator/Xcode.h
type XcodeSaveOptions (line 11) | enum XcodeSaveOptions {
type XcodeSaveOptions (line 16) | typedef enum XcodeSaveOptions XcodeSaveOptions;
type XcodeSchemeActionResultStatus (line 19) | enum XcodeSchemeActionResultStatus {
type XcodeSchemeActionResultStatus (line 27) | typedef enum XcodeSchemeActionResultStatus XcodeSchemeActionResultStatus;
FILE: canviz-0.1/canviz.js
function debug (line 809) | function debug(str, escape) {
FILE: canviz-0.1/path/path.js
function interpolate (line 213) | function interpolate(p0, p1) {
function collapse (line 224) | function collapse(ns) {
FILE: canviz-0.1/prototype/prototype.js
function klass (line 50) | function klass() {
function $A (line 812) | function $A(iterable) {
function $w (line 945) | function $w(string) {
function $H (line 993) | function $H(object) {
function toQueryPair (line 999) | function toQueryPair(key, value) {
function $ (line 1513) | function $(element) {
function stripAlpha (line 2289) | function stripAlpha(filter){
function extend (line 2627) | function extend(tagName) {
function copy (line 2634) | function copy(methods, destination, onlyIfAbsent) {
function findDOMClass (line 2644) | function findDOMClass(tagName) {
function $$ (line 3443) | function $$() {
function getEventID (line 3935) | function getEventID(element) {
function getDOMEventName (line 3941) | function getDOMEventName(eventName) {
function getCacheForID (line 3946) | function getCacheForID(id) {
function getWrappersForEventName (line 3950) | function getWrappersForEventName(id, eventName) {
function createWrapper (line 3955) | function createWrapper(element, eventName, handler) {
function findWrapper (line 3974) | function findWrapper(id, eventName, handler) {
function destroyWrapper (line 3979) | function destroyWrapper(id, eventName, handler) {
function destroyCache (line 3985) | function destroyCache() {
function fireContentLoadedEvent (line 4102) | function fireContentLoadedEvent() {
function iter (line 4251) | function iter(name) {
Condensed preview — 32 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (466K chars).
[
{
"path": ".gitignore",
"chars": 34,
"preview": "*html/*\n*xcuserdata*\n*xccheckout*\n"
},
{
"path": "LICENSE",
"chars": 1059,
"preview": "Copyright (c) 2017 John Holdsworth\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this"
},
{
"path": "README.md",
"chars": 2522,
"preview": "# Refactorator II, The App\n\nDue to chnages in the index databaase format in Xcode 10, this program no longer works prope"
},
{
"path": "Refactorator/AppController.swift",
"chars": 18171,
"preview": "//\n// AppState.swift\n// Refactorator\n//\n// Created by John Holdsworth on 13/12/2016.\n// Copyright © 2016 John Holdsw"
},
{
"path": "Refactorator/AppDelegate.swift",
"chars": 75502,
"preview": "//\n// AppDelegate.swift\n// Refactorator\n//\n// Created by John Holdsworth on 19/11/2016.\n// Copyright © 2016 John Hol"
},
{
"path": "Refactorator/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 903,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"mac\",\n \"size\" : \"16x16\",\n \"scale\" : \"1x\"\n },\n {\n \"idiom\" : "
},
{
"path": "Refactorator/Base.lproj/MainMenu.xib",
"chars": 76725,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3"
},
{
"path": "Refactorator/Common.swift",
"chars": 1724,
"preview": "//\n// Common.swift\n// Refactorator\n//\n// Created by John Holdsworth on 18/12/2016.\n// Copyright © 2016 John Holdswor"
},
{
"path": "Refactorator/Coverage.swift",
"chars": 1159,
"preview": "//\n// Coverage.swift\n// Refactorator\n//\n// Created by John Holdsworth on 03/03/2017.\n// Copyright © 2017 John Holdsw"
},
{
"path": "Refactorator/Credits.rtf",
"chars": 4832,
"preview": "{\\rtf1\\ansi\\ansicpg1252\\cocoartf1404\\cocoasubrtf470\n{\\fonttbl\\f0\\fswiss\\fcharset0 Helvetica;\\f1\\fnil\\fcharset0 LucidaGra"
},
{
"path": "Refactorator/Formatter.swift",
"chars": 18719,
"preview": "//\n// Formatter.swift\n// Refactorator\n//\n// Created by John Holdsworth on 13/12/2016.\n// Copyright © 2016 John Holds"
},
{
"path": "Refactorator/Info.plist",
"chars": 2826,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Refactorator/Integration.swift",
"chars": 3821,
"preview": "//\n// SubApp.swift\n// Refactorator\n//\n// Created by John Holdsworth on 21/12/2016.\n// Copyright © 2016 John Holdswor"
},
{
"path": "Refactorator/Intro.html",
"chars": 359,
"preview": "<style> #source { font: Arial: 10pt !important; } </style><apan style='font: Arial: 10pt important!;'>\n <b>Welcome to"
},
{
"path": "Refactorator/Project.swift",
"chars": 8619,
"preview": "//\n// Project.swift\n// Refactorator\n//\n// Created by John Holdsworth on 20/11/2016.\n// Copyright © 2016 John Holdswo"
},
{
"path": "Refactorator/Refactorator-Bridging-Header.h",
"chars": 203,
"preview": "//\n// Use this file to import your target's public headers that you would like to expose to Swift.\n//\n\n#import <sqlite3"
},
{
"path": "Refactorator/Source.html",
"chars": 5683,
"preview": "<html><head><meta charset=\"utf-8\"/><style>\n\nbody, table { font: 10pt Menlo Regular; }\n\n.builtin { color: #A90D91; }\n.co"
},
{
"path": "Refactorator/Utils.h",
"chars": 252,
"preview": "//\n// Utils.h\n// Refactorator\n//\n// Created by John Holdsworth on 19/11/2016.\n// Copyright © 2016 John Holdsworth. A"
},
{
"path": "Refactorator/Utils.m",
"chars": 2584,
"preview": "//\n// Utils.m\n// Refactorator\n//\n// Created by John Holdsworth on 19/11/2016.\n// Copyright © 2016 John Holdsworth. A"
},
{
"path": "Refactorator/Xcode.h",
"chars": 14612,
"preview": "/*\n * Xcode.h -- extracted using: sdef /Applications/Xcode.app | sdp -fh --basename Xcode and slightly modified for Swif"
},
{
"path": "Refactorator/canviz.html",
"chars": 2310,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "Refactorator/canviz2.html",
"chars": 2208,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "Refactorator/coverage.rb",
"chars": 528,
"preview": "#!/usr/bin/ruby\n\n# coverage.rb\n# Refactorator\n#\n# Created by John Holdsworth on 03/03/2017.\n# Copyright © 2017 John "
},
{
"path": "Refactorator.xcodeproj/project.pbxproj",
"chars": 23535,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "Refactorator.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 157,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:Refactorator.xc"
},
{
"path": "canviz-0.1/LICENSE.txt",
"chars": 1108,
"preview": "MIT-style software license for Canviz library\n\nCopyright (c) 2006-2009 Ryan Schmidt\n\nPermission is hereby granted, free "
},
{
"path": "canviz-0.1/README.txt",
"chars": 835,
"preview": "CANVIZ\n======\n\n\nIntroduction\n------------\n\nCanviz is a library for drawing Graphviz graphs to a web browser canvas. It i"
},
{
"path": "canviz-0.1/canviz.css",
"chars": 1057,
"preview": "/*\n * This file is part of Canviz. See http://www.canviz.org/\n * $Id: canviz.css 246 2008-12-27 08:36:24Z ryandesign.com"
},
{
"path": "canviz-0.1/canviz.js",
"chars": 25952,
"preview": "/*\n * This file is part of Canviz. See http://www.canviz.org/\n * $Id: canviz.js 265 2009-05-19 13:35:13Z ryandesign.com "
},
{
"path": "canviz-0.1/path/path.js",
"chars": 15012,
"preview": "// $Id: path.js 262 2009-05-19 11:55:24Z ryandesign.com $\n\nvar Point = Class.create({\n\tinitialize: function(x, y) {\n\t\tth"
},
{
"path": "canviz-0.1/prototype/prototype.js",
"chars": 129738,
"preview": "/* Prototype JavaScript framework, version 1.6.0.3\n * (c) 2005-2008 Sam Stephenson\n *\n * Prototype is freely distribu"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the johnno1962/RefactoratorApp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 32 files (432.4 KB), approximately 133.2k tokens, and a symbol index with 28 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.