Skip to content

Instantly share code, notes, and snippets.

@dirkstoop
Created May 19, 2011 10:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dirkstoop/980499 to your computer and use it in GitHub Desktop.
Save dirkstoop/980499 to your computer and use it in GitHub Desktop.
Versions script that supports native Word comparisons
#!/usr/bin/python
import os, sys
# more imports inline below, for better performance we only import "What you need, when you need it" (just like those domain-squatting websites)
"""
Requires: Mac OS X 10.5 or later (or PyObjC installed on 10.4)
Created by Dirk Stoop/Sofa BV on 2009-09-02.
No rights reserved, no warrantees, do what you want with this script.
Purpose:
Tries to find a suitable app to open files in, everything that doesn't match with one of the extensions
provided here, or that cannot be opened succesfully (e.g. because the specified app can't be found) will
be opened with Kaleidoscope.
Known Issues:
1. Some apps will only open one of the files if they were not running yet when the script is invoked
this is a bug in the individual app or in NSWorkspace's openFile:withApplication:.
2. Some file types are diverted to other apps by Versions before the script is invoked (pdf and png go to Preview.app),
if this is something that bugs you, let us know and we'll change that behavior if enough people need it to change.
"""
extensionToAppDict = {}
# use the below lines as a template to add your own apps for file extensions,
# you can either provide just one application name with u'' around it, or give a list of application names,
# as in the Photoshop example. [u'app1', u'app2']
# The latter approach is handy when people might have different versions of an app, that have different names
# if a list of app names is given, the script will try all of them left-to-right until it finds a matching app.
# CUSTOMIZE THIS:
extensionToAppDict[u'.doc'] = u'Microsoft Word'
extensionToAppDict[u'.docx'] = u'Microsoft Word'
extensionToAppDict[u'.xls'] = u'Microsoft Excel'
extensionToAppDict[u'.xlsx'] = u'Microsoft Excel'
extensionToAppDict[u'.pages'] = u'Pages'
extensionToAppDict[u'.numbers'] = u'Numbers'
extensionToAppDict[u'.graffle'] = u'OmniGraffle'
# extensionToAppDict[u'.jpg'] = [u'Adobe Photoshop CS4', u'Adobe Photoshop CS3', u'Adobe Photoshop CS2']
# extensionToAppDict[u'.psd'] = [u'Adobe Photoshop CS4', u'Adobe Photoshop CS3', u'Adobe Photoshop CS2']
# set this to False if you don't want to try using Word's 'compare' feature
useNativeWordComparison = True
# the rest is just code to do stuff with these mappings
for key in extensionToAppDict.keys():
extensionToAppDict[key.lower()] = extensionToAppDict[key]
def applicationNamesForFileExtension(theFileExtension):
# this always returns a list of app names, even if there's only one
applicationNames = []
if theFileExtension in extensionToAppDict.keys():
applicationNames = extensionToAppDict[theFileExtension]
if isinstance(applicationNames, basestring):
applicationNames = [applicationNames]
return applicationNames
def compareFilePaths():
filePaths = [sys.argv[1], sys.argv[2]]
extensionToCheck = os.path.splitext(filePaths[0])[1].lower()
# special case word files
if useNativeWordComparison:
if extensionToCheck in [u'.doc', u'.docx']:
someAppleScript = u"""
tell application "Microsoft Word"
activate
set doc1 to "%s" as POSIX file
open doc1
set doc2 to "%s" as POSIX file
compare active document path doc2 author name "Versions Comparison" ignore all comparison warnings True
end tell
""" % (filePaths[1], filePaths[0])
from AppKit import NSAppleScript
script = NSAppleScript.alloc().initWithSource_(someAppleScript)
error = script.executeAndReturnError_(None)[1]
if not error:
return
sys.stderr.write('Failed to compare documents in Word, we\'re just going to open both...\nAppleScript:\n%s\n\nAppleScript error:\n%s' % (someAppleScript, error))
# we only use the first file's extension to figure out which app to use
# so if you try to compare a .doc file to a .psd, Word will be opened
applicationNames = applicationNamesForFileExtension(extensionToCheck)
filesWereOpened = False
for appName in applicationNames:
try:
from AppKit import NSWorkspace
except ImportError:
sys.stderr.write('Fail in %s, could not import AppKit' % __file__)
break
# try to open both files with the application, if this fails, we'll try with the next application
fileOneWasOpened = NSWorkspace.sharedWorkspace().openFile_withApplication_(filePaths[0], appName)
fileTwoWasOpened = NSWorkspace.sharedWorkspace().openFile_withApplication_(filePaths[1], appName)
if fileOneWasOpened and fileTwoWasOpened:
# if both files could be opened, stop
filesWereOpened = True
break
if not filesWereOpened:
try:
from Foundation import NSTask
except ImportError:
sys.stderr.write('Fail in %s, could not import Foundation' % __file__)
sys.exit(1)
# if no suitable app could open both of the files for whatever reason, we pass them on to Kaleidoscope
kaleidoscopeToolPath = '/usr/local/bin/ksdiff'
if not os.path.exists(kaleidoscopeToolPath):
kaleidoscopeToolPath = u'/Applications/Kaleidoscope.app/Contents/MacOS/ksdiff'
if not os.path.exists(kaleidoscopeToolPath):
kaleidoscopeToolPath = os.popen("which ksdiff").read().strip()
task = NSTask.launchedTaskWithLaunchPath_arguments_(kaleidoscopeToolPath, (filePaths[0], filePaths[1]))
task.waitUntilExit()
if __name__ == '__main__':
compareFilePaths()
@dirkstoop
Copy link
Author

Instructions

Download this file, unzip it, and copy "Automatic.py" to /Library/Application Support/Versions/Compare Scripts/ ("" stands for the path to your home directory). You should now be able to select it from Versions' preferences window.

The first time you open a comparison, Versions will ask you to make the script executable, click "Change" in the dialog that appears.

Performance notes

Because this script imports the Foundation or AppKit framework from Python, it needs to parse BridgeSupport files every time you use it. All you need to know about that is that this makes it a bit slower (not much, but noticable nonetheless) than letting Versions use a file comparison application directly. Any somewhat experienced Cocoa developer should be able to port this script to a native Objective-C Foundation command-line tool with little effort. Replacing it with a compiled Foundation command line tool that does the exact same will alleviate the performance issue.

If anyone feels like doing that, please add a link to the source of your port below so people can use that instead. Consider the above Python script to be in the public domain. And consider the "warrantees" in the script source to be "warranties" (oops, typo).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment