Skip to content

Editor Plugins

Michael Schenk edited this page Jun 29, 2016 · 2 revisions

Plug-ins are very simple to write. In a nutshell, you subscribe to key editor events and call gtm record <path/file> in response to those events.

The best way to learn is by example. Here's the Sublime plug-in.

import sublime
import sublime_plugin
import sys
import time
import os
import subprocess

def find_gtm_path():
    if sys.platform == 'win32':
        exe = 'gtm.exe'
        path_sep = ";"
        pf = os.path.join(os.environ.get("ProgramFiles", ""), "gtm", "bin")
        pfx86 = os.path.join(os.environ.get("ProgramFiles(x86)", ""), "gtm", "bin")
        default_path = pf + path_sep + pfx86
    else:
        exe = 'gtm'
        path_sep = ":"
        default_path = "/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/"

    env_path = set(os.environ['PATH'].split(path_sep))
    paths = env_path.union(set(default_path.split(path_sep)))

    if os.path.isfile(exe):
        return exe
    else:
        for p in paths:
            f = os.path.join(p, exe)
            if os.path.isfile(f):
                return f

    return None

class GTM(sublime_plugin.EventListener):
    update_interval = 30
    last_update = 0.0
    last_path = None

    gtm_path = find_gtm_path()
    
    no_gtm_err = ("GTM executable not found\n"
                  "Install GTM and/or update your system path\n"
                  "Make sure to restart Sublime after install\n"
                  "See https://www.github.com/git-time-metric/gtm")

    record_err = ("GTM error saving time\n"
                  "Install GTM and/or update your system path\n"
                  "Make sure to restart Sublime after install\n"
                  "See https://www.github.com/git-time-metric/gtm")

    if not gtm_path:
        sublime.error_message(no_gtm_err)

    def on_post_save_async(self, view):
        self.record(view, view.file_name())

    def on_modified_async(self, view):
        self.record(view, view.file_name())

    def on_selection_modified_async(self, view):
        self.record(view, view.file_name())

    def on_activated_async(self, view):
        self.record(view, view.file_name())

    def record(self, view, path):

        if GTM.gtm_path and path and (
            path != GTM.last_path or
            time.time() - GTM.last_update > GTM.update_interval ):

            GTM.last_update = time.time()
            GTM.last_path = path

            cmd = '{0} record "{1}"'.format(GTM.gtm_path, path)
            return_code = subprocess.call(cmd, shell=True)

            if return_code != 0:
                sublime.error_message(GTM.record_err)

First thing to determine is what events to subscribe to for an editor. Typically you want to listen for saving, gain focus, keyboard and sometimes mouse events. The specific event names and when they are triggered will vary slightly by editor.

However you want to only call gtm record when necessary. If we are looking at the same file from the last event do nothing if it has been less than 30 seconds, otherwise call gtm record <path/file> and save the current path/file to compare to the next triggered event.

Keep in mind the gtm record command is fast and you shouldn't notice any delays. If you do, something is not correct with the implementation of the plugin-in.

Another concern when writing plug-ins is how you determine where the gtm executable is installed. Try using the logic in find_gtm_path() if possible.

Lastly let the user know when you can't find the gtm executable and that they may need to install gtm or update their path and where to go for further information.

That's it!