Commits

Anonymous committed 42f7fcf

Added "follow ups", linked headlines, reworked external link opener to support plugins, added several plugins, added support for Windows in link opener and updated readme.

  • Participants
  • Parent commits 6e0a9c8

Comments (0)

Files changed (10)

 * Features
   Here's a list of all the features implemented so far:
 
-  - Context sensitive highlighting
+  * Context sensitive highlighting
     - Headlines
-    - Pages
-    - Breaks
+    - Pages. E.g.:
+      --- This is a page marker ---
+    - Breaks. E.g.:
+      ~ This is a break.
     - Tacks
+    - Follow ups. E.g.:
+      -> Lorem ipsum.
+      => Lorem ipsum.
     - Checkboxes
     - Checkbox summaries
     - External links
-    - Inter document links
-  - Context sensitive actions
+    - Inter document links. E.g. {1} {{Context sensitive actions}}
+
+  * Context sensitive actions
     - Toggle checkbox on pressing enter
     - Auto update of checkbox summary on toggle of checkboxes
     - Recalc number of children in checkbox summary on pressing enter
-    - External link opener on pressing enter (e.g. [[http://www.google.de]])
-      (currently only working on OSX)
-    - Jumping between inter document links on pressing enter (e.g. {1})
+    - External link opener on pressing enter
+      (currently only working on OSX and Windows)
+      - Plugin-system including aliases
+      - Plugin: Jira
+        - Normal call: [[jira:PLAYGROUND]]
+        - Alias j: [[j:PLAYGROUND-123]]
+      - Plugin: Crucible code review
+        - Normal call: [[crucible:CR-123]]
+        - Aliases cru and cr: [[cr:CR-123]]
+      - Plugin: FishEye repo incl. opt. changeset
+        - Normal call: [[fisheye:some_repo]]
+        - Normal call: [[fisheye:some_repo/revision_or_tag]]
+        - Aliases fish and fe: [[fe:some_repo/123]]
+      - Plugin: eMail
+        - Create call: [[mailto:ok@ryotic.de]]
+        - Create call with subject: [[mailto:ok@ryotic.de/some subject]]
+    - Jump between inter document links on pressing enter (e.g. {1})
+    - Jump to linked headline on pressing enter (e.g. {{Installation}})
 
 * Installation
 
-** Codebase
-   Clone or copy the contents into a Packages/orgmode folder.
+  * Codebase
+    Clone or copy the contents into a Packages/orgmode folder.
 
-** Key bindings for interaction
-   See "Key bindings for interaction" under "Attachments" below for details.
+  * Key bindings for interaction
+    See {{Key bindings for interaction}} under {{Attachments}} below for details.
 
-** Theme additions for proper syntax highlighting
-   See "Theme additions for proper syntax highlighting" under "Attachments" below for details.
+  * Theme additions for proper syntax highlighting
+    See {{Theme additions for proper syntax highlighting}} under {{Attachments}} below  for details.
 
-* Todo [1/10]
-  - [ ] External link opener on pressing enter [1/3]
+* Todo [0/19]
+  - [ ] External link opener on pressing enter [2/3]
     - [X] on OSX
     - [ ] on Linux
-    - [ ] on Windows
-  - [X] Recalc number of children in checkbox summary on pressing enter.
+    - [X] on Windows
   - [ ] When (un-)checking *all* checkboxes of siblings, toggle parent checkbox.
   - [ ] Either make highlight_code_remarks.py configurable thru view settings so that orgmode can control its regex patterns or fork/extend it to archive an equal goal.
-  - [ ] Export into formatted text file
+  - [ ] Export into formatted text file [0/1]
+    - [ ] Format: Markdown
   - [ ] Automatic export after save into given format if mark found in org file.
-  - [ ] Fix subsequent indents
   - [ ] Fix cursor position after filling checkbox summary on checkbox toggle
   - [ ] Tab trigger "[]" which extends into "[ ] " and updates the summary
   - [ ] Define special block/area in document where time logging can occur. If one changes the status (TODO, WORKING, DONE etc.) this will be written into the log. Ideas for format are welcome!
+  - [ ] If all children don't have checkboxes only show the number of children in the summary
+  - [ ] If the summary ends with "%]" calculate percentage instead of amount
+  - [ ] If multiple checkboxes are in one line only work with the one under the cursor
+  - [ ] If multiple checkbox summaries are in one line only work with the one under the cursor
+  - [ ] If mutliple checkbox summaries are in one line update every one of them independently on updating a child checkbox
+  - [ ] Move key bindings out of readme into its own file
+  - [ ] Move theme additions out of readme into its own file
+  - [ ] Implement external link plugin: eMail [2/3]
+    - [ ] Open call [[email:ok@ryotic.de/inbox/some title]]
+    - [X] Create call [[mailto:ok@ryotic.de]]
+    - [X] Create call with subject [[mailto:ok@ryotic.de/some subject]]
+  - [ ] Pressing return on a TODO chain shall set it to DONE
+  - [ ] ASCII tables.
+  - [ ] Code remark collector. Recursively scans a specified folder for files with given filename pattern for code remarks and shows them as a list. Should be realized with begin and end markers to support later update on pressing enter on either marker.
 
 * Known Issues
   - When creating an empty checkbox summary and hitting an checkbox below the cursor will be placed a little bit to the left. Seems like the selection is being rebuilt incorrectly due to the added chars for the summary.
-  - Subsequent indent of wrapped paragraphs don't respect stars, tacks and checkboxes.
-  
+  - Subsequent indent of wrapped paragraphs don't respect stars, tacks, checkboxes, follow ups etc..
+    -> [[http://sublimetext.userecho.com/feedback/26943-/]]
+
 * External links
   {1} Homepage [[https://bitbucket.org/theblacklion/sublime_orgmode/]]
   {2} Issue tracker [[https://bitbucket.org/theblacklion/sublime_orgmode/issues?status=new&status=open]]
 
 * Attachments
 
-** Key bindings for interaction
-   Put the following into your key bindings file:
+  * Key bindings for interaction
+    Put the following into your key bindings file:
 
-   [code]
-   { "keys": ["enter"], "command": "orgmode_toggle_checkbox", "context":
-     [
-       { "key": "selector", "operator": "equal", "operand": "orgmode.checkbox" }
-     ]
-   },
-   { "keys": ["enter"], "command": "orgmode_recalc_checkbox_summary", "context":
-     [
-       { "key": "selector", "operator": "equal", "operand":  "orgmode.checkbox.summary" }
-     ]
-   },
-   {  "keys": ["enter"], "command": "orgmode_open_link", "con text":
+    [code]
+    { "keys": ["enter"], "command": "orgmode_toggle_checkbox", "context":
       [
-         { "key": "selector", "operator": "equal", "operand":  "orgmode.link" }
+        { "key": "selector", "operator": "equal", "operand": "orgmode.checkbox" }
       ]
-   },
-   {  "keys": ["enter"], "command": "orgmode_cycle_internal_link", "context":
+    },
+    { "keys": ["enter"], "command": "orgmode_recalc_checkbox_summary", "context":
       [
-         { "key": "selector", "operator": "equal", "operand":  "orgmode.link.internal" }
-     ]
-   }
-   [/code]
+        { "key": "selector", "operator": "equal", "operand":  "orgmode.checkbox.summary" }
+      ]
+    },
+    {  "keys": ["enter"], "command": "orgmode_open_link", "con text":
+      [
+        { "key": "selector", "operator": "equal", "operand":  "orgmode.link" }
+      ]
+    },
+    {  "keys": ["enter"], "command": "orgmode_cycle_internal_link", "context":
+      [
+        { "key": "selector", "operator": "equal", "operand":  "orgmode.link.internal" }
+      ]
+    }
+    [/code]
 
-** Theme additions for proper syntax highlighting
-   Put the following into your theme file:
+  * Theme additions for proper syntax highlighting
+    Put the following into your theme file:
 
-   [code]
-   <dict>
-       <key>name</key>
-       <string>orgmode link</string>
-       <key>scope</key>
-       <string>orgmode.link</string>
-       <key>settings</key>
-       <dict>
-         <key>foreground</key>
-         <string>#FB9A4B</string>
-         <key>fontStyle</key>
-         <string>underline</string>
-      </dict>
-   </dict>
-   <dict>
-       <key>name</key>
-       <string>orgmode page</string>
-       <key>scope</key>
-       <string>orgmode.page</string>
-       <key>settings</key>
-       <dict>
-         <key>foreground</key>
-         <string>#FFFFAA</string>
-      </dict>
-   </dict>
-   <dict>
-       <key>name</key>
-       <string>orgmode break</string>
-       <key>scope</key>
-       <string>orgmode.break</string>
-       <key>settings</key>
-       <dict>
-         <key>foreground</key>
-         <string>#FFAAAA</string>
-      </dict>
-   </dict>
-   <dict>
-       <key>name</key>
-       <string>orgmode headline</string>
-       <key>scope</key>
-       <string>orgmode.headline</string>
-       <key>settings</key>
-       <dict>
-         <key>foreground</key>
-         <string>#9EFFFF</string>
-      </dict>
-   </dict>
-   <dict>
-       <key>name</key>
-       <string>orgmode tack</string>
-       <key>scope</key>
-       <string>orgmode.tack</string>
-       <key>settings</key>
-       <dict>
-         <key>foreground</key>
-         <string>#FFFFAA</string>
-      </dict>
-   </dict>
-   <dict>
-       <key>name</key>
-       <string>orgmode checkbox</string>
-       <key>scope</key>
-       <string>orgmode.checkbox</string>
-       <key>settings</key>
-       <dict>
-         <key>foreground</key>
-         <string>#FFFFAA</string>
-      </dict>
-   </dict>
-   <dict>
-       <key>name</key>
-       <string>orgmode checkbox summary</string>
-       <key>scope</key>
-       <string>orgmode.checkbox.summary</string>
-       <key>settings</key>
-       <dict>
-         <key>foreground</key>
-         <string>#FFFFAA</string>
-      </dict>
-   </dict>
-   [/code]
+    [code]
+    <dict>
+        <key>name</key>
+        <string>orgmode link</string>
+        <key>scope</key>
+        <string>orgmode.link</string>
+        <key>settings</key>
+        <dict>
+          <key>foreground</key>
+          <string>#FB9A4B</string>
+          <key>fontStyle</key>
+          <string>underline</string>
+       </dict>
+    </dict>
+    <dict>
+        <key>name</key>
+        <string>orgmode page</string>
+        <key>scope</key>
+        <string>orgmode.page</string>
+        <key>settings</key>
+        <dict>
+          <key>foreground</key>
+          <string>#FFFFAA</string>
+       </dict>
+    </dict>
+    <dict>
+        <key>name</key>
+        <string>orgmode break</string>
+        <key>scope</key>
+        <string>orgmode.break</string>
+        <key>settings</key>
+        <dict>
+          <key>foreground</key>
+          <string>#FFAAAA</string>
+       </dict>
+    </dict>
+    <dict>
+        <key>name</key>
+        <string>orgmode headline</string>
+        <key>scope</key>
+        <string>orgmode.headline</string>
+        <key>settings</key>
+        <dict>
+          <key>foreground</key>
+          <string>#9EFFFF</string>
+       </dict>
+    </dict>
+    <dict>
+        <key>name</key>
+        <string>orgmode tack</string>
+        <key>scope</key>
+        <string>orgmode.tack</string>
+        <key>settings</key>
+        <dict>
+          <key>foreground</key>
+          <string>#FFFFAA</string>
+       </dict>
+    </dict>
+    <dict>
+        <key>name</key>
+        <string>orgmode follow up</string>
+        <key>scope</key>
+        <string>orgmode.follow_up</string>
+        <key>settings</key>
+        <dict>
+          <key>foreground</key>
+          <string>#FFFFAA</string>
+       </dict>
+    </dict>
+    <dict>
+        <key>name</key>
+        <string>orgmode checkbox</string>
+        <key>scope</key>
+        <string>orgmode.checkbox</string>
+        <key>settings</key>
+        <dict>
+          <key>foreground</key>
+          <string>#FFFFAA</string>
+       </dict>
+    </dict>
+    <dict>
+        <key>name</key>
+        <string>orgmode checkbox summary</string>
+        <key>scope</key>
+        <string>orgmode.checkbox.summary</string>
+        <key>settings</key>
+        <dict>
+          <key>foreground</key>
+          <string>#FFFFAA</string>
+       </dict>
+    </dict>
+    <dict>
+        <key>name</key>
+        <string>orgmode tags</string>
+        <key>scope</key>
+        <string>orgmode.tags</string>
+        <key>settings</key>
+        <dict>
+          <key>foreground</key>
+          <string>#AAFFAA</string>
+       </dict>
+    </dict>
+    [/code]
-import os
-import subprocess
+'''
+Settings in Global.sublime-settings are:
+- orgmode.open_link.resolvers: See DEFAULT_OPEN_LINK_RESOLVERS.
+- orgmode.open_link.resolver.abstract.commands: See DEFAULT_OPEN_LINK_COMMANDS in resolver.abstract.
+For more settings see headers of specific resolvers.
+'''
+
+import sys
 import re
 
 import sublime
 import sublime_plugin
 
 
-OPEN_LINK_COMMAND = ['open']
+DEFAULT_OPEN_LINK_RESOLVERS = [
+    'jira',
+    'crucible',
+    'fisheye',
+    'email',
+    'local_file',
+]
+
+
+def find_resolvers():
+    from os.path import splitext
+    from glob import glob
+    path = 'resolver'
+    files = glob('%s/*.py' % path)
+    available_resolvers = dict()
+    for pos, file_ in enumerate(files[:]):
+        name = splitext(file_)[0].replace('/', '.')
+        __import__(name)
+        module = reload(sys.modules[name])
+        if '__init__' in file_ or 'abstract' in file_:
+            continue
+        name = name.split('.').pop()
+        # print name, module
+        available_resolvers[name] = module
+    return available_resolvers
+available_resolvers = find_resolvers()
 
 
 class OrgmodeOpenLinkCommand(sublime_plugin.TextCommand):
-    '''
-    @todo: If the link is a local org-file open it via sublime, otherwise use OPEN_LINK_COMMAND.
-    @todo: Implement mechanisms for Linux and Windows.
-    '''
+
+    def __init__(self, *args, **kwargs):
+        super(OrgmodeOpenLinkCommand, self).__init__(*args, **kwargs)
+        settings = sublime.load_settings('Global.sublime-settings')
+        wanted_resolvers = settings.get('orgmode.open_link.resolvers', DEFAULT_OPEN_LINK_RESOLVERS)
+        self.resolvers = [available_resolvers[name].Resolver(self.view) \
+                          for name in wanted_resolvers]
+
+    def resolve(self, content):
+        for resolver in self.resolvers:
+            result = resolver.resolve(content)
+            if result is not None:
+                return resolver, result
+        return None, None
 
     def run(self, edit):
         view = self.view
             content = view.substr(region)
             if content.startswith('[[') and content.endswith(']]'):
                 content = content[2:-2]
-            content = os.path.expandvars(content)
-            content = os.path.expanduser(content)
-            cmd = OPEN_LINK_COMMAND + [content]
-            process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-            stdout, stderr = process.communicate()
-            if stdout:
-                sublime.status_message(stdout)
-            if stderr:
-                sublime.error_message(stderr)
+            resolver, content = self.resolve(content)
+            if content is None:
+                sublime.error_message('Could not resolve link:\n%s' % content)
+                continue
+            resolver.execute(content)
 
 
 class OrgmodeCycleInternalLinkCommand(sublime_plugin.TextCommand):
             return
         region = view.extract_scope(sel.end())
         content = view.substr(region).strip()
+        if content.startswith('{{') and content.endswith('}}'):
+            content = '* %s' % content[2:-2]
         found = self.view.find(content, region.end(), sublime.LITERAL)
         if not found:  # Try wrapping around buffer.
             found = self.view.find(content, 0, sublime.LITERAL)
         if not found or same:
             sublime.status_message('No sibling found for: %s' % content)
             return
+        found = view.extract_scope(found.begin())
         sels.clear()
         sels.add(sublime.Region(found.begin()))
         try:

File orgmode.tmLanguage

 			<key>name</key>
 			<string>orgmode.page</string>
 			<key>match</key>
-			<string>^(\-\-\-) .*\n?$</string>
+			<string>^\s*(\-\-\-) .*\n?$</string>
 		</dict>
 		<dict>
 			<key>name</key>
 			<string>orgmode.break</string>
 			<key>match</key>
-			<string>^(\~)+ .*\n?$</string>
+			<string>^\s*(\~)+ .*\n?$</string>
 		</dict>
 		<dict>
 			<key>name</key>
 			<string>orgmode.headline</string>
 			<key>match</key>
-			<string>^\s*(\*)+ [^\[\]]*</string>
+			<string>^\s*(\*)+ [^\[\]:]*</string>
 		</dict>
 		<dict>
 			<key>name</key>
 		</dict>
 		<dict>
 			<key>name</key>
+			<string>orgmode.follow_up</string>
+			<key>match</key>
+			<string>^\s*(\-\>|\=\>) </string>
+		</dict>
+		<dict>
+			<key>name</key>
 			<string>orgmode.checkbox</string>
 			<key>match</key>
 			<string>(\[[xX ]\])\s?</string>
 			<key>name</key>
 			<string>orgmode.link</string>
 			<key>match</key>
-			<string>\[\[(.+)\]\]</string>
+			<string>\[\[(.+?)\]\]</string>
 		</dict>
 		<dict>
 			<key>name</key>
-			<string>orgmode.link.internal</string>
+			<string>orgmode.link.internal.number</string>
 			<key>match</key>
 			<string>\{(\d+)\}</string>
 		</dict>
 		<dict>
+			<key>name</key>
+			<string>orgmode.link.internal.headline</string>
+			<key>match</key>
+			<string>\{\{(.+?)\}\}</string>
+		</dict>
+		<dict>
 			<key>contentName</key>
 			<string>text source</string>
 			<key>begin</key>
 			<key>end</key>
 			<string>\s*\[/code\]</string>
 		</dict>
+		<dict>
+			<key>name</key>
+			<string>orgmode.tags</string>
+			<key>match</key>
+			<string>:[\w\d:]+:</string>
+		</dict>
 	</array>
 	<key>scopeName</key>
 	<string>text.orgmode</string>

File resolver/__init__.py

Empty file added.

File resolver/abstract.py

+'''
+Settings in Global.sublime-settings are:
+- orgmode.open_link.resolver.abstract.commands: See DEFAULT_OPEN_LINK_COMMANDS.
+'''
+
+import sys
+import subprocess
+
+import sublime
+
+
+DEFAULT_OPEN_LINK_COMMANDS = dict(
+    # Standard universal can opener for OSX.
+    darwin=['open'],
+    
+    # Found at:
+    # - http://frank.zinepal.com/open-a-file-in-the-default-application-using
+    # - http://commandwindows.com/tipsandtricks.htm#startcommand
+    windows=['cmd', '/c', 'start'],
+
+    # Dunno yet - help anyone?
+    # linux= FIXME ???
+)
+
+
+class AbstractLinkResolver(object):
+
+    def __init__(self, view):
+        super(AbstractLinkResolver, self).__init__()
+        self.view = view
+        self.settings = sublime.load_settings('Global.sublime-settings')
+        self.link_commands = self.settings.get('orgmode.open_link.resolver.abstract.commands', DEFAULT_OPEN_LINK_COMMANDS)
+
+    def extract(self, content):
+        return content
+
+    def replace(self, content):
+        return content
+
+    def resolve(self, content):
+        match = self.extract(content)
+        if not match:
+            return None
+        return self.replace(match)
+    
+    def get_link_command(self):
+        platform = sys.platform
+        for key, val in self.link_commands.iteritems():
+            if key in platform:
+                return val
+        return None
+
+    def execute(self, content):
+        command = self.get_link_command()
+        if not command:
+            sublime.error_message('Could not get link opener command.\nPlatform not yet supported.')
+        content = content.encode('utf-8')
+        cmd = command + [content]
+        # print '*****'
+        # print repr(content), content
+        # print repr(cmd)
+        # print cmd
+        sublime.status_message('Executing: %s' % cmd)
+        process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        stdout, stderr = process.communicate()
+        if stdout:
+            stdout = unicode(stdout, 'utf-8')
+            sublime.status_message(stdout)
+        if stderr:
+            stderr = unicode(stderr, 'utf-8')
+            sublime.error_message(stderr)
+
+
+
+class AbstractRegexLinkResolver(AbstractLinkResolver):
+
+    def __init__(self, view):
+        super(AbstractRegexLinkResolver, self).__init__(view)
+        self.regex = None
+
+    def extract(self, content):
+        if self.regex is None:
+            return content
+        match = self.regex.match(content)
+        return match
+
+    def replace(self, match):
+        return match.groups()[1]

File resolver/crucible.py

+'''
+Settings in Global.sublime-settings are:
+- orgmode.open_link.resolver.crucible.pattern: See PATTERN_DEFAULT.
+- orgmode.open_link.resolver.crucible.url: See URL_DEFAULT.
+'''
+
+import re
+
+from abstract import AbstractRegexLinkResolver
+
+
+PATTERN_SETTING = 'orgmode.open_link.resolver.crucible.pattern'
+PATTERN_DEFAULT = r'^(crucible|cru|cr):(?P<review>.+)$'
+URL_SETTING = 'orgmode.open_link.resolver.crucible.url'
+URL_DEFAULT = 'http://sandbox.fisheye.atlassian.com/cru/%s'
+
+
+class Resolver(AbstractRegexLinkResolver):
+
+    def __init__(self, view):
+        super(Resolver, self).__init__(view)
+        get = self.settings.get
+        pattern = get(PATTERN_SETTING, PATTERN_DEFAULT)
+        self.regex = re.compile(pattern)
+        self.url = get(URL_SETTING, URL_DEFAULT)
+
+    def replace(self, match):
+        return self.url % match.group('review')

File resolver/email.py

+'''
+Settings in Global.sublime-settings are:
+- orgmode.open_link.resolver.email.pattern: See PATTERN_DEFAULT.
+- orgmode.open_link.resolver.email.url: See URL_DEFAULT.
+'''
+
+import re
+
+from abstract import AbstractRegexLinkResolver
+
+
+PATTERN_SETTING = 'orgmode.open_link.resolver.email.pattern'
+PATTERN_DEFAULT = r'^(?P<type>email|mailto):(?P<email>[^/]+)(/(?P<subject>.+))?$'
+URL_SETTING = 'orgmode.open_link.resolver.email.url'
+URL_DEFAULT = 'mailto:%s'
+
+
+class Resolver(AbstractRegexLinkResolver):
+
+    def __init__(self, view):
+        super(Resolver, self).__init__(view)
+        get = self.settings.get
+        pattern = get(PATTERN_SETTING, PATTERN_DEFAULT)
+        self.regex = re.compile(pattern)
+        self.url = get(URL_SETTING, URL_DEFAULT)
+
+    def replace(self, match):
+        match = match.groupdict()
+        # print match
+        if match['type'] == 'mailto':
+            url = self.url % match['email']
+            if match['subject']:
+                url += '?subject=%s' % match['subject']
+            return url
+        import sublime
+        # TODO Implement email opener here.
+        sublime.error_message('Email opener not implemented yet.')
+        raise NotImplemented()

File resolver/fisheye.py

+'''
+Settings in Global.sublime-settings are:
+- orgmode.open_link.resolver.fisheye.pattern: See PATTERN_DEFAULT.
+- orgmode.open_link.resolver.fisheye.url: See URL_DEFAULT.
+'''
+
+import re
+
+from abstract import AbstractRegexLinkResolver
+
+
+PATTERN_SETTING = 'orgmode.open_link.resolver.fisheye.pattern'
+PATTERN_DEFAULT = r'^(fisheye|fish|fe):(?P<repo>[^/]+)(/(?P<rev>.+))?$'
+URL_SETTING = 'orgmode.open_link.resolver.fisheye.url'
+URL_DEFAULT = 'http://sandbox.fisheye.atlassian.com/changelog/%s'
+
+
+class Resolver(AbstractRegexLinkResolver):
+
+    def __init__(self, view):
+        super(Resolver, self).__init__(view)
+        get = self.settings.get
+        pattern = get(PATTERN_SETTING, PATTERN_DEFAULT)
+        self.regex = re.compile(pattern)
+        self.url = get(URL_SETTING, URL_DEFAULT)
+
+    def replace(self, match):
+        match = match.groupdict()
+        url = self.url % match['repo']
+        if match['rev']:
+            url += '?cs=%s' % match['rev']
+        return url

File resolver/jira.py

+'''
+Settings in Global.sublime-settings are:
+- orgmode.open_link.resolver.jira.pattern: See PATTERN_DEFAULT.
+- orgmode.open_link.resolver.jira.url: See URL_DEFAULT.
+'''
+
+import re
+
+from abstract import AbstractRegexLinkResolver
+
+
+PATTERN_SETTING = 'orgmode.open_link.resolver.jira.pattern'
+PATTERN_DEFAULT = r'^(jira|j):(?P<issue>.+)$'
+URL_SETTING = 'orgmode.open_link.resolver.jira.url'
+URL_DEFAULT = 'http://sandbox.onjira.com/browse/%s'
+
+
+class Resolver(AbstractRegexLinkResolver):
+
+    def __init__(self, view):
+        super(Resolver, self).__init__(view)
+        get = self.settings.get
+        pattern = get(PATTERN_SETTING, PATTERN_DEFAULT)
+        self.regex = re.compile(pattern)
+        self.url = get(URL_SETTING, URL_DEFAULT)
+
+    def replace(self, match):
+        return self.url % match.group('issue')

File resolver/local_file.py

+import os
+
+from abstract import AbstractLinkResolver
+
+
+class Resolver(AbstractLinkResolver):
+    '''
+    @todo: If the link is a local org-file open it directly via sublime, otherwise use OPEN_LINK_COMMAND.
+    '''
+
+    def expand_path(self, filepath):
+        filepath = os.path.expandvars(filepath)
+        filepath = os.path.expanduser(filepath)
+
+        drive, filepath = os.path.splitdrive(filepath)
+        if not filepath.startswith('/'):  # If filepath is relative...
+            cwd = os.path.dirname(self.view.file_name())
+            testfile = os.path.join(cwd, filepath)
+            if os.path.exists(testfile):  # See if it exists here...
+                filepath = testfile
+
+        return ':'.join([drive, filepath]) if drive else filepath
+
+    def replace(self, content):
+        content = self.expand_path(content)
+        return content