Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full Sublime Text 3 compatibility. #69

Merged
merged 3 commits into from Aug 3, 2013
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
149 changes: 99 additions & 50 deletions gist.py
@@ -1,4 +1,4 @@
from __future__ import print_function
import sublime import sublime
import sublime_plugin import sublime_plugin
import os import os
Expand All @@ -13,40 +13,69 @@
import contextlib import contextlib
import shutil import shutil
import re import re
import codecs


try: try:
import urllib2 as urllib import urllib2 as urllib
except ImportError: # Python 3 except ImportError: # Python 3
import urllib.request as urllib import urllib.request as urllib


DEFAULT_CREATE_PUBLIC_VALUE = 'false'
DEFAULT_USE_PROXY_VALUE = 'false' global settings
settings = sublime.load_settings('Gist.sublime-settings') global DEFAULT_CREATE_PUBLIC_VALUE
GISTS_URL = 'https://api.github.com/gists' global DEFAULT_USE_PROXY_VALUE
USER_GISTS_URL = 'https://api.github.com/users/%s/gists' global GISTS_URL
ORGS_URL = 'https://api.github.com/user/orgs' global USER_GISTS_URL
ORG_MEMBERS_URL = 'https://api.github.com/orgs/%s/members' global ORGS_URL

global ORG_MEMBERS_URL
#Enterprise support:
if settings.get('enterprise'):
GISTS_URL = settings.get('url') def initialize_globals():
if not GISTS_URL: '''
raise MissingCredentialsException() Initialize globals. In Sublime Text 3 this can no longer me done in
GISTS_URL += '/api/v3/gists' the module scope.


#Per page support (max 100) See "Restricted API Usage at Startup" in the following document.
if settings.get('max_gists'): http://www.sublimetext.com/docs/3/porting_guide.html
if settings.get('use_starred'): '''
GISTS_URL += '/starred' global settings
USER_GISTS_URL += '/starred' global DEFAULT_CREATE_PUBLIC_VALUE

global DEFAULT_USE_PROXY_VALUE
if settings.get('max_gists') <= 100: global GISTS_URL
MAX_GISTS = '?per_page=%d' % settings.get('max_gists') global USER_GISTS_URL
GISTS_URL += MAX_GISTS global ORGS_URL
USER_GISTS_URL += MAX_GISTS global ORG_MEMBERS_URL
else:
settings.set( "max_gists",100 ) settings = sublime.load_settings('Gist.sublime-settings')
sublime.status_message("Gist: GitHub API does not support a value of higher than 100") DEFAULT_CREATE_PUBLIC_VALUE = 'false'
DEFAULT_USE_PROXY_VALUE = 'false'
GISTS_URL = 'https://api.github.com/gists'
USER_GISTS_URL = 'https://api.github.com/users/%s/gists'
ORGS_URL = 'https://api.github.com/user/orgs'
ORG_MEMBERS_URL = 'https://api.github.com/orgs/%s/members'

#Enterprise support:
if settings.get('enterprise'):
GISTS_URL = settings.get('url')
if not GISTS_URL:
raise MissingCredentialsException()
GISTS_URL += '/api/v3/gists'

#Per page support (max 100)
if settings.get('max_gists'):
if settings.get('use_starred'):
GISTS_URL += '/starred'
USER_GISTS_URL += '/starred'

if settings.get('max_gists') <= 100:
MAX_GISTS = '?per_page=%d' % settings.get('max_gists')
GISTS_URL += MAX_GISTS
USER_GISTS_URL += MAX_GISTS
else:
settings.set('max_gists', 100)
sublime.status_message(
"Gist: GitHub API does not support a value of higher than 100")



class MissingCredentialsException(Exception): class MissingCredentialsException(Exception):
pass pass
Expand All @@ -70,8 +99,8 @@ def get_credentials():
return (username, password) return (username, password)


def basic_auth_string(): def basic_auth_string():
auth_string = u'%s:%s' % get_credentials() auth_string = '%s:%s' % get_credentials()
return auth_string.encode('utf-8') return bytes(auth_string, 'ascii')


def get_token(): def get_token():
token = settings.get('token') token = settings.get('token')
Expand All @@ -80,13 +109,13 @@ def get_token():
return token return token


def token_auth_string(): def token_auth_string():
auth_string = u'%s' % get_token() auth_string = '%s' % get_token()
return auth_string.encode('utf-8') return auth_string


if sublime.platform() == 'osx': if sublime.platform() == 'osx':
# Keychain support # Keychain support
# Instead of Gist.sublime-settings, fetch username and password from the user's github.com keychain entry # Instead of Gist.sublime-settings, fetch username and password from the user's github.com keychain entry
SERVER = 'github.com' SERVER = b'github.com'


def create_keychain_accessor(): def create_keychain_accessor():
from ctypes import cdll, util, c_uint32, c_int, c_char_p, c_void_p, POINTER, pointer, byref, Structure, string_at from ctypes import cdll, util, c_uint32, c_int, c_char_p, c_void_p, POINTER, pointer, byref, Structure, string_at
Expand Down Expand Up @@ -187,7 +216,7 @@ def _fn(*args, **kwargs):
except SimpleHTTPError as err: except SimpleHTTPError as err:
msg = "Gist: GitHub returned error %d" % err.code msg = "Gist: GitHub returned error %d" % err.code
try: try:
response_json = json.loads(err.response) response_json = json.loads(err.response.decode('ascii'))
response_msg = response_json.get('message') response_msg = response_json.get('message')
if response_msg: if response_msg:
msg += ": " + response_msg msg += ": " + response_msg
Expand All @@ -200,7 +229,7 @@ def _fn(*args, **kwargs):
return _fn return _fn


def create_gist(public, description, files): def create_gist(public, description, files):
file_data = dict((filename, {'content': text}) for filename, text in files.items()) file_data = dict((filename, {'content': text}) for filename, text in list(files.items()))
data = json.dumps({'description': description, 'public': public, 'files': file_data}) data = json.dumps({'description': description, 'public': public, 'files': file_data})
gist = api_request(GISTS_URL, data) gist = api_request(GISTS_URL, data)
return gist return gist
Expand Down Expand Up @@ -243,9 +272,9 @@ def open_gist(gist_url):


gistify_view(view, gist, gist_filename) gistify_view(view, gist, gist_filename)


edit = view.begin_edit() view.run_command('append', {
view.insert(edit, 0, gist['files'][gist_filename]['content']) 'characters': gist['files'][gist_filename]['content'],
view.end_edit(edit) })


if not "language" in gist['files'][gist_filename]: continue if not "language" in gist['files'][gist_filename]: continue


Expand All @@ -268,12 +297,9 @@ def insert_gist(gist_url):
files = sorted(gist['files'].keys()) files = sorted(gist['files'].keys())
for gist_filename in files: for gist_filename in files:
view = sublime.active_window().active_view() view = sublime.active_window().active_view()
edit = view.begin_edit() view.run_command('insert', {
for region in view.sel(): 'characters': gist['files'][gist_filename]['content'],

})
view.replace(edit, region, gist['files'][gist_filename]['content'])

view.end_edit(edit)


def get_gists(): def get_gists():
return api_request(GISTS_URL) return api_request(GISTS_URL)
Expand Down Expand Up @@ -336,12 +362,15 @@ def api_request_native(url, data=None, method=None):
try: try:
request.add_header('Authorization', 'token ' + token_auth_string()) request.add_header('Authorization', 'token ' + token_auth_string())
except MissingTokenException: except MissingTokenException:
request.add_header('Authorization', 'Basic ' + base64.urlsafe_b64encode(basic_auth_string())) request.add_header(
'Authorization',
'Basic ' + str(base64.urlsafe_b64encode(basic_auth_string())))

request.add_header('Accept', 'application/json') request.add_header('Accept', 'application/json')
request.add_header('Content-Type', 'application/json') request.add_header('Content-Type', 'application/json')


if data is not None: if data is not None:
request.add_data(data) request.add_data(bytes(data, 'ASCII'))


if settings.get('https_proxy'): if settings.get('https_proxy'):
opener = urllib.build_opener(urllib.HTTPHandler(), urllib.HTTPSHandler(), opener = urllib.build_opener(urllib.HTTPHandler(), urllib.HTTPSHandler(),
Expand All @@ -354,7 +383,8 @@ def api_request_native(url, data=None, method=None):
if response.code == 204: # No Content if response.code == 204: # No Content
return None return None
else: else:
return json.loads(response.read()) return json.loads(response.read().decode('ascii'))

except urllib.HTTPError as err: except urllib.HTTPError as err:
with contextlib.closing(err): with contextlib.closing(err):
raise SimpleHTTPError(err.code, err.read()) raise SimpleHTTPError(err.code, err.read())
Expand Down Expand Up @@ -411,7 +441,7 @@ def api_request_curl(url, data=None, method=None):
if responsecode == 204: # No Content if responsecode == 204: # No Content
return None return None
elif 200 <= responsecode < 300 or responsecode == 100: # Continue elif 200 <= responsecode < 300 or responsecode == 100: # Continue
return json.loads(response) return json.loads(response.decode('ascii'))
else: else:
raise SimpleHTTPError(responsecode, response) raise SimpleHTTPError(responsecode, response)


Expand All @@ -425,6 +455,8 @@ def mode(self):


@catch_errors @catch_errors
def run(self, edit): def run(self, edit):
initialize_globals()

try: try:
get_token() get_token()
except MissingTokenException: except MissingTokenException:
Expand Down Expand Up @@ -470,7 +502,7 @@ def on_gist_filename(filename):
sublime.status_message("%s Gist: %s" % (self.mode(), gist_html_url)) sublime.status_message("%s Gist: %s" % (self.mode(), gist_html_url))


if gistify: if gistify:
gistify_view(self.view, gist, gist['files'].keys()[0]) gistify_view(self.view, gist, list(gist['files'].keys())[0])
# else: # else:
# open_gist(gist['url']) # open_gist(gist['url'])


Expand All @@ -483,6 +515,9 @@ class GistViewCommand(object):
def is_enabled(self): def is_enabled(self):
return self.gist_url() is not None return self.gist_url() is not None


def run(self, edit):
initialize_globals()

def gist_url(self): def gist_url(self):
return self.view.settings().get("gist_url") return self.view.settings().get("gist_url")


Expand All @@ -497,14 +532,18 @@ def gist_description(self):


class GistCopyUrl(GistViewCommand, sublime_plugin.TextCommand): class GistCopyUrl(GistViewCommand, sublime_plugin.TextCommand):
def run(self, edit): def run(self, edit):
GistViewCommand.run(self, edit)
sublime.set_clipboard(self.gist_html_url()) sublime.set_clipboard(self.gist_html_url())


class GistOpenBrowser(GistViewCommand, sublime_plugin.TextCommand): class GistOpenBrowser(GistViewCommand, sublime_plugin.TextCommand):
def run(self, edit): def run(self, edit):
GistViewCommand.run(self, edit)
webbrowser.open(self.gist_html_url()) webbrowser.open(self.gist_html_url())


class GistRenameFileCommand(GistViewCommand, sublime_plugin.TextCommand): class GistRenameFileCommand(GistViewCommand, sublime_plugin.TextCommand):
def run(self, edit): def run(self, edit):
GistViewCommand.run(self, edit)

old_filename = self.gist_filename() old_filename = self.gist_filename()


@catch_errors @catch_errors
Expand All @@ -520,6 +559,8 @@ def on_filename(filename):


class GistChangeDescriptionCommand(GistViewCommand, sublime_plugin.TextCommand): class GistChangeDescriptionCommand(GistViewCommand, sublime_plugin.TextCommand):
def run(self, edit): def run(self, edit):
GistViewCommand.run(self, edit)

@catch_errors @catch_errors
def on_gist_description(description): def on_gist_description(description):
if description and description != self.gist_description(): if description and description != self.gist_description():
Expand All @@ -536,6 +577,8 @@ def on_gist_description(description):
class GistUpdateFileCommand(GistViewCommand, sublime_plugin.TextCommand): class GistUpdateFileCommand(GistViewCommand, sublime_plugin.TextCommand):
@catch_errors @catch_errors
def run(self, edit): def run(self, edit):
GistViewCommand.run(self, edit)

text = self.view.substr(sublime.Region(0, self.view.size())) text = self.view.substr(sublime.Region(0, self.view.size()))
changes = {self.gist_filename(): {'content': text}} changes = {self.gist_filename(): {'content': text}}
update_gist(self.gist_url(), changes) update_gist(self.gist_url(), changes)
Expand All @@ -544,6 +587,8 @@ def run(self, edit):
class GistDeleteFileCommand(GistViewCommand, sublime_plugin.TextCommand): class GistDeleteFileCommand(GistViewCommand, sublime_plugin.TextCommand):
@catch_errors @catch_errors
def run(self, edit): def run(self, edit):
GistViewCommand.run(self, edit)

changes = {self.gist_filename(): None} changes = {self.gist_filename(): None}
update_gist(self.gist_url(), changes) update_gist(self.gist_url(), changes)
ungistify_view(self.view) ungistify_view(self.view)
Expand All @@ -552,6 +597,8 @@ def run(self, edit):
class GistDeleteCommand(GistViewCommand, sublime_plugin.TextCommand): class GistDeleteCommand(GistViewCommand, sublime_plugin.TextCommand):
@catch_errors @catch_errors
def run(self, edit): def run(self, edit):
GistViewCommand.run(self, edit)

gist_url = self.gist_url() gist_url = self.gist_url()
api_request(gist_url, method='DELETE') api_request(gist_url, method='DELETE')
for window in sublime.windows(): for window in sublime.windows():
Expand All @@ -568,6 +615,8 @@ class GistListCommandBase(object):


@catch_errors @catch_errors
def run(self, *args): def run(self, *args):
initialize_globals()

filtered = gists_filter(get_gists()) filtered = gists_filter(get_gists())
self.gists = filtered[0] self.gists = filtered[0]
gist_names = filtered[1] gist_names = filtered[1]
Expand Down