Created
November 4, 2011 17:20
-
-
Save ssbarnea/1339907 to your computer and use it in GitHub Desktop.
Python script that enables and handles edit:// hyperlinks under Windows.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys, os, subprocess, shutil | |
import traceback | |
from _winreg import * | |
from ctypes import c_int, WINFUNCTYPE, windll | |
from ctypes.wintypes import HWND, LPCSTR, UINT | |
prototype = WINFUNCTYPE(c_int, HWND, LPCSTR, LPCSTR, UINT) | |
paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", None), (1, "flags", 0) | |
MessageBox = prototype(("MessageBoxA", windll.user32), paramflags) | |
# use flag=1 to make it Yes|No | |
# See http://msdn.microsoft.com/en-us/library/windows/desktop/ms645505(v=vs.85).aspx) | |
""" | |
iexplore calls with full URL: edit://some | |
chrome calls with: //some | |
""" | |
# possible locations for source code, it is used for matching the path from inside the URL with the local one | |
# order is important | |
roots = [ | |
"C:/Automation/", | |
"C:/Python_workspace/trunk/", | |
"C:/AutomationScripts/", | |
"C:/_autoscripts/", | |
"C:/dev/auto", | |
'~/workspace', # ~ is your %HOME% dude ;) | |
] | |
def getJetBrainsPath(): | |
""" | |
Returns the path to the newest executable of the installed version of one of the JetBrains tools (PyCharm or IDEA). | |
If nothing is found it will return None. | |
""" | |
products = ['PyCharm','IntelliJ IDEA'] # made PyCharm first but it should be adter IDEA | |
exes = ["\\bin\\pycharm.exe", "\\bin\\idea.exe"] | |
for i in range(len(products)): | |
try: | |
key = OpenKey(HKEY_CURRENT_USER, "Software\\JetBrains\\%s" % products[i]) | |
versions = [] | |
try: | |
j = 0 | |
while True: | |
versions.append(EnumKey(key,j)) | |
j+=1 | |
except: | |
pass | |
versions.sort(reverse=True) | |
for ver in versions: | |
key = OpenKey(HKEY_CURRENT_USER, "Software\\JetBrains\\%s\\%s" % (products[i],ver)) | |
path = QueryValue(key, None) | |
if path: | |
path = path + exes[i] | |
if os.path.isfile(path): | |
return path | |
except Exception, e: | |
#print e | |
#traceback.print_exc() | |
pass | |
return None | |
def getNotepadPlusPlusPath(): | |
try: | |
key = OpenKey(HKEY_LOCAL_MACHINE, "Software\\Notepad++") | |
path = QueryValue(key, None) | |
if path: | |
path = path + "\\notepad++.exe" | |
if os.path.isfile(path): | |
return path | |
except Exception, e: | |
print e | |
traceback.print_exc() | |
pass | |
return None | |
def getEditor(): | |
""" | |
Returns the full path to a editor. It will pick one of: | |
* PyCharm | |
* IntelliJ IDEA | |
* Eclipse | |
* Notepad++ | |
* Notepad (bleah!) | |
""" | |
# 1: JetBrains tools have priority because they are commercial tools (and better) | |
jet = getJetBrainsPath() | |
if jet: | |
return jet | |
# 2: Eclipse sucks big time, they don't even have an installer and impossible to properly detect it's location, let's guess | |
editors = [ # don't play with the order, it will change the app being used to open the files | |
"C:\\dev\\eclipse\\eclipse.exe", | |
"${PROGRAMFILES}\\eclipse\\eclipse.exe", | |
"C:\\eclipse\\eclipse.exe", | |
"${PROGRAMFILES}\\eclipse37\\eclipse.exe", | |
] | |
for i in range(len(editors)): | |
editor = os.path.abspath(os.path.expanduser(os.path.expandvars(editors[i]))) | |
if os.path.isdir(editor): | |
return editor | |
# 3: Notepad++ | |
editor = getNotepadPlusPlusPath() | |
if editor: | |
return editor | |
# 4: fallback, the infamous `notepad` | |
return 'notepad.exe' | |
def sanitize(filename): | |
""" | |
Use this function to auto-correct bad paths. | |
For example if you keep the code in another location than the server. | |
""" | |
filename = os.path.abspath(filename) | |
if not os.path.isfile(filename): | |
for local_root in local_roots: | |
for root in roots: | |
if filename.find(root, 0)==0: # if the URL-path starts with our root | |
tmp = filename.replace(root, local_root) | |
if os.path.isfile(tmp): | |
return tmp | |
return filename | |
if __name__ == '__main__': | |
local_roots = [] | |
for i in range(len(roots)): | |
roots[i] = os.path.abspath(os.path.expanduser(roots[i])) | |
if os.path.isdir(roots[i]): | |
local_roots.append(roots[i]) | |
if not local_roots: | |
raise Exception("Unable to detect any local root directory in `roots` list: \n\n%s" % ("\n".join(roots))) | |
# print local_root | |
print getEditor() | |
cmd = " ".join(sys.argv) | |
try: | |
if len(sys.argv)<2: | |
# Called without parameters, this will trigger the build-in installer | |
p = subprocess.Popen('where python', stdout=subprocess.PIPE) | |
python = p.communicate()[0].split()[0] # first line | |
src = os.path.abspath(sys.argv[0]) | |
dst = os.path.dirname(python) + "\\" + os.path.basename(sys.argv[0]) | |
#print src | |
#print dst | |
if src != dst: | |
# not it's clear that we are supposed to install, but we need admin priviledges for this | |
import pythoncom | |
import pywintypes | |
import win32api | |
from win32com.shell import shell | |
result = MessageBox( | |
text="Do you want me to install edit:// protocol handler script?" \ | |
"\n\nInstall source: %s" \ | |
"\nInstall destination: %s" % (src, dst), | |
caption=sys.argv[0], | |
flags = 1) | |
if result == 1: | |
try: | |
shutil.copyfile(src, dst) | |
# install protocol handler | |
key = CreateKey(HKEY_CLASSES_ROOT, "edit") | |
SetValue(key, None, REG_SZ, "URL:edit Protocol") | |
key = CreateKey(HKEY_CLASSES_ROOT, "edit\\shell\\open\\command") | |
SetValue(key, None, REG_SZ, 'pythonw "%s" "%%1"' % dst) | |
key = CreateKey(HKEY_CLASSES_ROOT, "edit\\shell\\edit\\command") | |
SetValue(key, None, REG_SZ, 'pythonw "%s" "%%1"' % dst) | |
except e: | |
if not shell.IsUserAnAdmin(): | |
raise(str(e) + "\n\nOne possible reason could be the lack of priviledges, so please run me as Administrator in order to be able to install.") | |
sys.exit(0) | |
else: | |
MessageBox(text="Called without filename parameter and the script is already installed.\n\nSo there is nothing to do!", caption=sys.argv[0]) | |
sys.exit(0) | |
filename = sys.argv[1].split("edit://")[1].strip('/') | |
filename = os.path.abspath(filename) | |
origFilename = filename | |
filename = sanitize(filename) | |
if not os.path.isfile(filename): | |
MessageBox(text="File not found: %s\n\nOriginal filename: %s" % (filename, origFilename), caption=sys.argv[0]) | |
else: | |
try: | |
os.startfile(filename, "edit") | |
except WindowsError, e: | |
if e.winerror == 1155: # filetype has no associated 'edit' action. | |
editor = getEditor() | |
cmd = '"%s" "%s"' % (editor, filename) | |
print cmd | |
print subprocess.call(cmd) | |
# TODO: open the file in some editor, any editor instead of giving an error. | |
except Exception, e: | |
msg = str(e) + "\n\n" + traceback.format_exc() | |
MessageBox(text=msg, caption=cmd) | |
sys.exit(-1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment