#!/usr/bin/env python2
# coding: utf-8

# Handles urls like code://xyz

import sys
import os
from os.path import join, exists, normpath, basename
from subprocess import check_call
try:
    from urlparse import urlparse, parse_qs
except:
    from urllib.parse import urlparse, parse_qs


# Add paths where editors are likely found
print(os.environ['PATH'])
os.environ['PATH'] += os.pathsep + '/usr/local/bin'

if sys.platform == 'darwin':
    editorCommands = {
        'atom': ['open', '-a', 'atom', '-n', '--args'],
        # vscode doesn't honor arguments from "open -a"
        'vscode': [
            '/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code',
            '--goto', '--reuse-window',
        ],
    }
else:
    editorCommands = {
        'atom': ['atom'],
        # vscode doesn't honor arguments from "open -a"
        'vscode': [
            'code', '--goto', '--reuse-window',
        ],
    }


# Paths known to be repositories
repoCache = None


def enumerateRepos(home=None):
    ' Walk through HOME and yield git repositories. '
    if not home:
        home = os.environ.get('HOME', '.')
    # Walk all directories
    for dirpath, childDirs, filenames in os.walk(home, topdown=True):
        # Skip hidden directories
        if basename(dirpath).startswith('.'):
            del childDirs[:]
            continue
        # See whether that's a repository root
        if '.git' in childDirs or '.hg' in childDirs:
            print('GIT ' + basename(dirpath) + ' in ' + dirpath)
            yield dirpath


def collectRepos(home=None):
    global repoCache
    if repoCache is None:
        repoCache = list(enumerateRepos(home))
    return repoCache


def findRepoWithPath(path, repoName=None):
    ' Find a repo that contains this path. '
    for root in collectRepos():
        fullPath = root + '/' + path
        if exists(fullPath):
            if repoName and basename(root).lower() != repoName.lower():
                print('W Repo name %s do not match %s', repoName, root)
            return root
    return None


def findRepoFromUrl(url):
    # Find erroneous encoding of hashes (#)
    lastHash = url.rfind('%23')
    if lastHash >= 0:
        url = url[:lastHash] + '#' + url[lastHash+3:]

    # Parse url and parameters
    purl = urlparse(url)
    params = parse_qs(purl.query)
    if not purl.path:
        return None

    root = findRepoWithPath(
        path=purl.path,
        repoName=purl.hostname,
    )
    if root is None:
        return None

    lines = params.get('line') or params.get('l')
    cols = params.get('column') or params.get('c')
    location = {
        'root': root,
        'path': purl.path,
        'line': lines[0] if lines else None,
        'column': cols[0] if cols else None,
    }
    return location


def launchEditor(location):
    arg = location['root'] + '/' + location['path']
    if location['line']:
        arg += ':' + location['line']
        if location['column']:
            arg += ':' + location['column']

    # TODO Use multiple PATH to support linux and mac
    editor = 'atom'
    cmd = editorCommands[editor] + [arg]
    check_call(cmd)


def openUrl(url):
    print('Opening ' + url)
    location = findRepoFromUrl(url)
    print(location)
    if not location:
        print('E Not found')
    else:
        launchEditor(location)


def testOpen():
    openUrl('code://deckard/codebase/Makefile?line=2&column=4')


def main(argv):
    if len(argv) >= 2:
        # Run once, from argument
        openUrl(argv[1])
    else:
        # Run forever, from stdin
        while True:
            url = sys.stdin.readline().strip()
            openUrl(url)
            sys.stdout.flush()

if __name__ == '__main__':
    main(sys.argv)

@@ -278,16 +278,55 @@
 arse_qs%0A
+import json%0A%0ACONFIG_FILE = 'dcode.json'
 %0A%0A# Add 
@@ -3471,19 +3471,675 @@
 def 
-main(argv):
+load():%0A    try:%0A        with open(CONFIG_FILE) as fd:%0A            config = json.load(fd)%0A    except:%0A        config = %7B%7D%0A    return config%0A%0A%0Adef save(config):%0A    try:%0A        with open(CONFIG_FILE, 'w') as fd:%0A            json.dump(config, fd)%0A    except Exception as e:%0A        print(repr(e))%0A    return config%0A%0A%0Adef init():%0A    ' Load configuration file and populate the cache if available '%0A    global repoCache%0A    config = load()%0A    repos = config.get('repositories')%0A    if repos:%0A        repoCache = repos%0A    else:%0A        config%5B'repositories'%5D = collectRepos()%0A    return config%0A%0A%0Adef main(argv):%0A    ' Command line '%0A    config = init()%0A    save(config)
 %0A   

Store available repos in a config file