feat(SublimeText2.GitPackages): cache packages

This commit is contained in:
Iristyle
2013-04-04 08:55:33 -04:00
parent c3efdad2c2
commit c0f9c6d45a
109 changed files with 15317 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
*.pyc
*.cache
*.sublime-project
package-metadata.json

View File

@@ -0,0 +1,6 @@
[
{
"caption": "File: Refresh",
"command": "side_bar_git_refresh_tab_contents_by_running_command_again"
}
]

View File

@@ -0,0 +1,177 @@
[
{ "caption": "-" , "id":"side-bar-end-separator"},
{
"caption": "Git ", "id":"side-bar-git",
"children":
[
{ "caption": "Add & Commit…", "command": "side_bar_git_add_commit", "args": {"paths": []} },
{ "caption": "Add & Commit & Push…", "command": "side_bar_git_add_commit_push", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Commit…", "command": "side_bar_git_commit", "args": {"paths": []} },
{ "caption": "Commit Undo", "command": "side_bar_git_commit_undo", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Commit Amend", "command": "side_bar_git_commit_amend", "args": {"paths": []} },
{ "caption": "Commit All…", "command": "side_bar_git_commit_all", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Status", "command": "side_bar_git_status", "args": {"paths": []} },
{ "caption": "-"},
{
"caption": "Diff", "id":"side-bar-git-diff",
"children":
[
{ "caption": "all changes since the last commit", "command": "side_bar_git_diff_all_changes_since_last_commit", "args": {"paths": []} },
{ "caption": "all changes since the last commit ( ignore whitespace )", "command": "side_bar_git_diff_all_changes_since_last_commit_ignore_white_space", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "changes that have not been staged", "command": "side_bar_git_diff_changes_not_staged", "args": {"paths": []} },
{ "caption": "changes that are staged but not committed", "command": "side_bar_git_diff_changes_staged_not_commited", "args": {"paths": []} },
{ "caption": "between the index and last commit", "command": "side_bar_git_diff_between_index_and_last_commit", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "between remote and last local commit (origin/master..)", "command": "side_bar_git_diff_between_remote_and_last_local_commit", "args": {"paths": []} },
{ "caption": "between last local commit and remote (..origin/master)", "command": "side_bar_git_diff_between_last_local_commit_and_remote", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "between the two latest tags", "command": "xxxx", "args": {"paths": []} }
]
},
{
"caption": "Difftool", "id":"side-bar-git-difftool",
"children":
[
{ "caption": "all changes since the last commit", "command": "side_bar_git_difftool_all_changes_since_last_commit", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "changes that have not been staged", "command": "side_bar_git_difftool_changes_not_staged", "args": {"paths": []} },
{ "caption": "changes that are staged but not committed", "command": "side_bar_git_difftool_changes_staged_not_commited", "args": {"paths": []} },
{ "caption": "between the index and last commit", "command": "side_bar_git_difftool_between_index_and_last_commit", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "between remote and last local commit (origin/master..)", "command": "side_bar_git_difftool_between_remote_and_last_local_commit", "args": {"paths": []} },
{ "caption": "between last local commit and remote (..origin/master)", "command": "side_bar_git_difftool_between_last_local_commit_and_remote", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "between the two latest tags", "command": "xxxx", "args": {"paths": []} }
]
},
{ "caption": "-"},
{
"caption": "Log", "id":"side-bar-git-log",
"children":
[
{ "caption": "short summary of changes last 30", "command": "side_bar_git_log_stat_short_latest", "args": {"paths": []} },
{ "caption": "short summary of changes full", "command": "side_bar_git_log_stat_short_full", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "summary of changes last 30", "command": "side_bar_git_log_stat_latest", "args": {"paths": []} },
{ "caption": "summary of changes full", "command": "side_bar_git_log_stat_full", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "list of changes last 50", "command": "side_bar_git_log_stat_list_latest", "args": {"paths": []} },
{ "caption": "list of changes last 50 with commit", "command": "side_bar_git_log_stat_list_commit_latest", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "diffs of changes last 30", "command": "side_bar_git_log_extended_latest", "args": {"paths": []} },
{ "caption": "diffs of changes full", "command": "side_bar_git_log_extended_full", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "summary of changes since latest tag", "command": "xxxx", "args": {"paths": []} },
{ "caption": "summary of changes since latest push", "command": "side_bar_git_log_since_latest_push", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "between the two latest tags", "command": "xxxx", "args": {"paths": []} }
]
},
{ "caption": "Reflog", "command": "side_bar_git_reflog", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Blame", "command": "side_bar_git_blame", "args": {"paths": []} },
{ "caption": "-"},
{
"caption": "Tags", "id":"side-bar-git-tags",
"children":
[
{ "caption": "Auto-Tag", "command": "xxxx", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Add…", "command": "xxxx", "args": {"paths": []} },
{ "caption": "Remove…", "command": "xxxx", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "List", "command": "xxxx", "args": {"paths": []} }
]
},
{ "caption": "-"},
{
"caption": "Revert", "id":"side-bar-git-revert",
"children":
[
{ "caption": "discard changes to tracked", "command": "side_bar_git_revert_tracked", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "discard changes to tracked, clean untracked", "command": "side_bar_git_revert_tracked_clean_untracked", "args": {"paths": []} },
{ "caption": "discard changes to tracked, clean untracked, unstage", "command": "side_bar_git_revert_tracked_clean_untracked_unstage", "args": {"paths": []} },
{ "caption": "discard changes to tracked, unstage, clean untracked", "command": "side_bar_git_revert_tracked_unstage_clean_untracked", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "unstage", "command": "side_bar_git_revert_unstage", "args": {"paths": []} }
]
},
{ "caption": "-"},
{
"caption": "Branch",
"children":
[
{ "caption": "new from current and switch to…", "command": "side_bar_git_branch_new_from_current", "args": {"paths": []} },
{ "caption": "new from master and switch to…", "command": "side_bar_git_branch_new_from_master", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "new from cleaned current and switch to…", "command": "side_bar_git_branch_new_from_clean_current", "args": {"paths": []} },
{ "caption": "new from cleaned master and switch to…", "command": "side_bar_git_branch_new_from_clean_master", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "switch to master", "command": "side_bar_git_branch_switch_to_master", "args": {"paths": []} },
{ "caption": "switch to…", "command": "side_bar_git_branch_switch_to", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "rebase current into master", "command": "side_bar_git_rebase_current_into_master", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "merge changes to current from…", "command": "side_bar_git_merge_to_current_from", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "delete…", "command": "side_bar_git_branch_delete", "args": {"paths": []} },
{ "caption": "delete even if unmerged (force deletion)…","command": "side_bar_git_branch_delete_force", "args": {"paths": []} }
]
},
{ "caption": "Checkout to…", "command": "side_bar_git_checkout_to", "args": {"paths": []} },
{ "caption": "Checkout repository to…", "command": "side_bar_git_checkout_repository_to", "args": {"paths": []} },
{ "caption": "-"},
{
"caption": "Push, Pull, Fetch", "id":"side-bar-git-pull-push-fetch",
"children":
[
{ "caption": "Push", "command": "side_bar_git_push", "args": {"paths": []} },
{ "caption": "Push All Branches", "command": "side_bar_git_push_all_branches", "args": {"paths": []} },
{ "caption": "Push, Push Tags", "command": "side_bar_git_push_and_push_tags", "args": {"paths": []} },
{ "caption": "Push Tags", "command": "side_bar_git_push_tags", "args": {"paths": []} },
{ "caption": "Push with options…", "command": "side_bar_git_push_with_options", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Pull", "command": "side_bar_git_pull", "args": {"paths": []} },
{ "caption": "Pull with options…", "command": "side_bar_git_pull_with_options", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Fetch", "command": "side_bar_git_fetch", "args": {"paths": []} },
{ "caption": "Fetch with options…", "command": "side_bar_git_fetch_with_options", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Remote add…", "command": "side_bar_git_remote_add", "args": {"paths": []} }
]
},
{ "caption": "-"},
{ "caption": "Clone…", "command": "side_bar_git_clone", "args": {"paths": []} },
{ "caption": "Init", "command": "side_bar_git_init", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Add", "command": "side_bar_git_add", "args": {"paths": []} },
{ "caption": "Remove", "command": "side_bar_git_remove", "args": {"paths": []} },
{ "caption": "Remove Keep Local", "command": "side_bar_git_remove_keep_local", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Add to Git Ignore", "command": "side_bar_git_ignore_add", "args": {"paths": []} },
{ "caption": "Open Git Ignore", "command": "side_bar_git_ignore_open", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Git GUI", "command": "side_bar_git_gui", "args": {"paths": []} },
{ "caption": "Gitk", "command": "side_bar_git_gitk", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Liberal Git Command", "command": "side_bar_git_liberal", "args": {"paths": []} }
]
},
{ "caption": "-", "id": "side-bar-end-separator" }
]

View File

@@ -0,0 +1,3 @@
[
{ "keys": ["f5"], "command": "side_bar_git_refresh_tab_contents_by_running_command_again" }
]

View File

@@ -0,0 +1,3 @@
[
{ "keys": ["f5"], "command": "side_bar_git_refresh_tab_contents_by_running_command_again" }
]

View File

@@ -0,0 +1,3 @@
[
{ "keys": ["f5"], "command": "side_bar_git_refresh_tab_contents_by_running_command_again" }
]

View File

@@ -0,0 +1,39 @@
[
{
"caption": "Preferences",
"mnemonic": "n",
"id": "preferences",
"children":
[
{
"caption": "Package Settings",
"mnemonic": "P",
"id": "package-settings",
"children":
[
{
"caption": "Side Bar Git",
"children":
[
{
"command": "open_file", "args":
{
"file": "${packages}/SideBarGit/SideBarGit.sublime-settings"
},
"caption": "Settings Default"
},
{
"command": "open_file", "args":
{
"file": "${packages}/User/SideBarGit.sublime-settings"
},
"caption": "Settings User"
},
{ "caption": "-" }
]
}
]
}
]
}
]

View File

@@ -0,0 +1,177 @@
[
{ "caption": "-" , "id":"side-bar-end-separator"},
{
"caption": "Git ", "id":"side-bar-git",
"children":
[
{ "caption": "Add & Commit…", "command": "side_bar_git_add_commit", "args": {"paths": []} },
{ "caption": "Add & Commit & Push…", "command": "side_bar_git_add_commit_push", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Commit…", "command": "side_bar_git_commit", "args": {"paths": []} },
{ "caption": "Commit Undo", "command": "side_bar_git_commit_undo", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Commit Amend", "command": "side_bar_git_commit_amend", "args": {"paths": []} },
{ "caption": "Commit All…", "command": "side_bar_git_commit_all", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Status", "command": "side_bar_git_status", "args": {"paths": []} },
{ "caption": "-"},
{
"caption": "Diff", "id":"side-bar-git-diff",
"children":
[
{ "caption": "all changes since the last commit", "command": "side_bar_git_diff_all_changes_since_last_commit", "args": {"paths": []} },
{ "caption": "all changes since the last commit ( ignore whitespace )", "command": "side_bar_git_diff_all_changes_since_last_commit_ignore_white_space", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "changes that have not been staged", "command": "side_bar_git_diff_changes_not_staged", "args": {"paths": []} },
{ "caption": "changes that are staged but not committed", "command": "side_bar_git_diff_changes_staged_not_commited", "args": {"paths": []} },
{ "caption": "between the index and last commit", "command": "side_bar_git_diff_between_index_and_last_commit", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "between remote and last local commit (origin/master..)", "command": "side_bar_git_diff_between_remote_and_last_local_commit", "args": {"paths": []} },
{ "caption": "between last local commit and remote (..origin/master)", "command": "side_bar_git_diff_between_last_local_commit_and_remote", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "between the two latest tags", "command": "xxxx", "args": {"paths": []} }
]
},
{
"caption": "Difftool", "id":"side-bar-git-difftool",
"children":
[
{ "caption": "all changes since the last commit", "command": "side_bar_git_difftool_all_changes_since_last_commit", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "changes that have not been staged", "command": "side_bar_git_difftool_changes_not_staged", "args": {"paths": []} },
{ "caption": "changes that are staged but not committed", "command": "side_bar_git_difftool_changes_staged_not_commited", "args": {"paths": []} },
{ "caption": "between the index and last commit", "command": "side_bar_git_difftool_between_index_and_last_commit", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "between remote and last local commit (origin/master..)", "command": "side_bar_git_difftool_between_remote_and_last_local_commit", "args": {"paths": []} },
{ "caption": "between last local commit and remote (..origin/master)", "command": "side_bar_git_difftool_between_last_local_commit_and_remote", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "between the two latest tags", "command": "xxxx", "args": {"paths": []} }
]
},
{ "caption": "-"},
{
"caption": "Log", "id":"side-bar-git-log",
"children":
[
{ "caption": "short summary of changes last 30", "command": "side_bar_git_log_stat_short_latest", "args": {"paths": []} },
{ "caption": "short summary of changes full", "command": "side_bar_git_log_stat_short_full", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "summary of changes last 30", "command": "side_bar_git_log_stat_latest", "args": {"paths": []} },
{ "caption": "summary of changes full", "command": "side_bar_git_log_stat_full", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "list of changes last 50", "command": "side_bar_git_log_stat_list_latest", "args": {"paths": []} },
{ "caption": "list of changes last 50 with commit", "command": "side_bar_git_log_stat_list_commit_latest", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "diffs of changes last 30", "command": "side_bar_git_log_extended_latest", "args": {"paths": []} },
{ "caption": "diffs of changes full", "command": "side_bar_git_log_extended_full", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "summary of changes since latest tag", "command": "xxxx", "args": {"paths": []} },
{ "caption": "summary of changes since latest push", "command": "side_bar_git_log_since_latest_push", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "between the two latest tags", "command": "xxxx", "args": {"paths": []} }
]
},
{ "caption": "Reflog", "command": "side_bar_git_reflog", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Blame", "command": "side_bar_git_blame", "args": {"paths": []} },
{ "caption": "-"},
{
"caption": "Tags", "id":"side-bar-git-tags",
"children":
[
{ "caption": "Auto-Tag", "command": "xxxx", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Add…", "command": "xxxx", "args": {"paths": []} },
{ "caption": "Remove…", "command": "xxxx", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "List", "command": "xxxx", "args": {"paths": []} }
]
},
{ "caption": "-"},
{
"caption": "Revert", "id":"side-bar-git-revert",
"children":
[
{ "caption": "discard changes to tracked", "command": "side_bar_git_revert_tracked", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "discard changes to tracked, clean untracked", "command": "side_bar_git_revert_tracked_clean_untracked", "args": {"paths": []} },
{ "caption": "discard changes to tracked, clean untracked, unstage", "command": "side_bar_git_revert_tracked_clean_untracked_unstage", "args": {"paths": []} },
{ "caption": "discard changes to tracked, unstage, clean untracked", "command": "side_bar_git_revert_tracked_unstage_clean_untracked", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "unstage", "command": "side_bar_git_revert_unstage", "args": {"paths": []} }
]
},
{ "caption": "-"},
{
"caption": "Branch",
"children":
[
{ "caption": "new from current and switch to…", "command": "side_bar_git_branch_new_from_current", "args": {"paths": []} },
{ "caption": "new from master and switch to…", "command": "side_bar_git_branch_new_from_master", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "new from cleaned current and switch to…", "command": "side_bar_git_branch_new_from_clean_current", "args": {"paths": []} },
{ "caption": "new from cleaned master and switch to…", "command": "side_bar_git_branch_new_from_clean_master", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "switch to master", "command": "side_bar_git_branch_switch_to_master", "args": {"paths": []} },
{ "caption": "switch to…", "command": "side_bar_git_branch_switch_to", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "rebase current into master", "command": "side_bar_git_rebase_current_into_master", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "merge changes to current from…", "command": "side_bar_git_merge_to_current_from", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "delete…", "command": "side_bar_git_branch_delete", "args": {"paths": []} },
{ "caption": "delete even if unmerged (force deletion)…","command": "side_bar_git_branch_delete_force", "args": {"paths": []} }
]
},
{ "caption": "Checkout to…", "command": "side_bar_git_checkout_to", "args": {"paths": []} },
{ "caption": "Checkout repository to…", "command": "side_bar_git_checkout_repository_to", "args": {"paths": []} },
{ "caption": "-"},
{
"caption": "Push, Pull, Fetch", "id":"side-bar-git-pull-push-fetch",
"children":
[
{ "caption": "Push", "command": "side_bar_git_push", "args": {"paths": []} },
{ "caption": "Push All Branches", "command": "side_bar_git_push_all_branches", "args": {"paths": []} },
{ "caption": "Push, Push Tags", "command": "side_bar_git_push_and_push_tags", "args": {"paths": []} },
{ "caption": "Push Tags", "command": "side_bar_git_push_tags", "args": {"paths": []} },
{ "caption": "Push with options…", "command": "side_bar_git_push_with_options", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Pull", "command": "side_bar_git_pull", "args": {"paths": []} },
{ "caption": "Pull with options…", "command": "side_bar_git_pull_with_options", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Fetch", "command": "side_bar_git_fetch", "args": {"paths": []} },
{ "caption": "Fetch with options…", "command": "side_bar_git_fetch_with_options", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Remote add…", "command": "side_bar_git_remote_add", "args": {"paths": []} }
]
},
{ "caption": "-"},
{ "caption": "Clone…", "command": "side_bar_git_clone", "args": {"paths": []} },
{ "caption": "Init", "command": "side_bar_git_init", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Add", "command": "side_bar_git_add", "args": {"paths": []} },
{ "caption": "Remove", "command": "side_bar_git_remove", "args": {"paths": []} },
{ "caption": "Remove Keep Local", "command": "side_bar_git_remove_keep_local", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Add to Git Ignore", "command": "side_bar_git_ignore_add", "args": {"paths": []} },
{ "caption": "Open Git Ignore", "command": "side_bar_git_ignore_open", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Git GUI", "command": "side_bar_git_gui", "args": {"paths": []} },
{ "caption": "Gitk", "command": "side_bar_git_gitk", "args": {"paths": []} },
{ "caption": "-"},
{ "caption": "Liberal Git Command", "command": "side_bar_git_liberal", "args": {"paths": []} }
]
},
{ "caption": "-", "id": "side-bar-end-separator" }
]

View File

@@ -0,0 +1,4 @@
{
"statusbar_branch" : true,
"path_to_git_unixes":"" //example: /usr/local/git/bin/git
}

View File

@@ -0,0 +1,170 @@
import sublime
import sublime_plugin
import functools
import threading
import subprocess
import sys
import re
import tempfile
import codecs
import os
import time
from SideBarGit import SideBarGit
from SideBarItem import SideBarItem
from Utils import Object
Object.running = False
Object.timing = time.time()
class SideBarGitGutterDiff(sublime_plugin.EventListener):
def on_load(self, view):
self.run(view)
def on_modified(self, view):
now = time.time()
if now - Object.timing > 0.1:
Object.timing = now
self.run(view)
else:
Object.timing = now
def on_post_save(self, view):
self.run(view)
def run(self, view):
if Object.running == False and view.file_name() != None and view.file_name() != '':
Object.running = True
# cache file repository ( if any )
if not view.settings().has('SideBarGitGutterRepository'):
item = SideBarItem(view.file_name(), False)
_item = SideBarItem(view.file_name(), False)
repos = SideBarGit().getSelectedRepos([_item])
if len(repos) > 0:
view.settings().set('SideBarGitGutterRepository', repos[0].repository.path())
view.settings().set('SideBarGitGutterCWD', repos[0].repository.path())
view.settings().set('SideBarGitGutterPath', item.forCwdSystemPathRelativeFrom(repos[0].repository.path()))
else:
view.settings().set('SideBarGitGutterRepository', '')
# if in a repo check for modifications
repo = view.settings().get('SideBarGitGutterRepository')
if repo != '':
SideBarGitGutterDiffThread(
view,
repo,
view.settings().get('SideBarGitGutterCWD'),
view.settings().get('SideBarGitGutterPath'),
view.substr(sublime.Region(0, view.size()))
).start()
else:
Object.running = False
class SideBarGitGutterDiffThread(threading.Thread):
def __init__(self, view, repo, cwd, path, content):
threading.Thread.__init__(self)
self.view = view
self.repo = repo
self.cwd = cwd
self.path = path
self.content = content
def run(self):
tmp = tempfile.NamedTemporaryFile(delete=False)
codecs.open(tmp.name, 'w+', 'utf-8').write(self.content)
comand = ['git', 'diff', '-p', '--unified=0', '--no-color', '--ignore-all-space', '--ignore-space-at-eol', '--ignore-space-change', 'HEAD:'+self.path, tmp.name]
process = subprocess.Popen(
comand,
cwd=self.cwd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=sys.platform == 'win32',
universal_newlines=True)
stdout, stderr = process.communicate()
if stdout != '' and stdout.find('fatal:') != 0:
hunk = re.finditer('\n@@ -([0-9]+),?([0-9]*) \+([0-9]+),?([0-9]*) @@', stdout)
additions = []
deletions = []
changes = []
for change in hunk:
g = []
for group in change.groups():
if group == '':
g.append(1)
else:
g.append(int(group))
deleted = g[1]
added = g[3]
if deleted == added and added == 1:
changes.append([g[2]-1, g[2]]);
else:
if deleted > 0:
if deleted == 1:
if added > deleted:
deletions.append([g[2], g[2]+deleted-added]);
else:
deletions.append([g[2], g[2]+1]);
else:
deletions.append([g[2]-1, g[2]+deleted-1])
if added > 0:
if added == 1:
additions.append([g[2], g[2]+added]);
else:
additions.append([g[2]-1, g[2]+added-1])
tmp.close();
os.remove(tmp.name)
sublime.set_timeout(functools.partial(self.add_regions, additions, deletions, changes), 0)
else:
tmp.close();
os.remove(tmp.name)
sublime.set_timeout(functools.partial(self.erase_regions), 0)
if stdout.find('fatal:'):
sublime.set_timeout(functools.partial(self.mark_not_in_a_repository), 0)
Object.running = False
def add_regions(self, additions, deletions, changes):
self.erase_regions()
rs = []
for r in additions:
while r[0] != r[1]:
rs.append(sublime.Region(self.view.text_point(r[0], 0)))
r[0] = r[0]+1
if len(rs):
self.view.add_regions("git.diff.additions", rs, "number", "dot", sublime.HIDDEN)
rs = []
for r in deletions:
while r[0] != r[1]:
rs.append(sublime.Region(self.view.text_point(r[0], 0)))
r[0] = r[0]+1
if len(rs):
self.view.add_regions("git.diff.deletions", rs, "entity.name.class", "dot", sublime.HIDDEN)
rs = []
for r in changes:
while r[0] != r[1]:
rs.append(sublime.Region(self.view.text_point(r[0], 0)))
r[0] = r[0]+1
if len(rs):
self.view.add_regions("git.diff.changes", rs, "string", "dot", sublime.HIDDEN)
def erase_regions(self):
self.view.erase_regions("git.diff.additions")
self.view.erase_regions("git.diff.deletions")
self.view.erase_regions("git.diff.changes")
def mark_not_in_a_repository(self):
self.view.settings().set('SideBarGitGutterRepository', '')

View File

@@ -0,0 +1,43 @@
import sublime, sublime_plugin
from sidebar.SideBarGit import SideBarGit
from sidebar.SideBarSelection import SideBarSelection
import threading
class Object():
pass
s = sublime.load_settings('SideBarGit.sublime-settings')
class StatusBarBranch(sublime_plugin.EventListener):
def on_load(self, v):
if s.get('statusbar_branch') and v.file_name():
StatusBarBranchGet(v.file_name(), v).start()
def on_activated(self, v):
if s.get('statusbar_branch') and v.file_name():
StatusBarBranchGet(v.file_name(), v).start()
class StatusBarBranchGet(threading.Thread):
def __init__(self, file_name, v):
threading.Thread.__init__(self)
self.file_name = file_name
self.v = v
def run(self):
for repo in SideBarGit().getSelectedRepos(SideBarSelection([self.file_name]).getSelectedItems()):
object = Object()
object.item = repo.repository
object.command = ['git', 'branch']
object.silent = True
SideBarGit().run(object)
sublime.set_timeout(lambda:self.on_done(SideBarGit.last_stdout.decode('utf-8')), 0)
return
def on_done(self, branches):
branches = branches.split('\n')
for branch in branches:
if branch.startswith("*"):
self.v.set_status('statusbar_sidebargit_branch', branch)
return

View File

@@ -0,0 +1,19 @@
"None are so hopelessly enslaved as those who falsely believe they are free."
Johann Wolfgang von Goethe
Copyright (C) 2012 Tito Bouzout <tito.bouzout@gmail.com>
This license apply to all the files inside this program unless noted
different for some files or portions of code inside these files.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation. http://www.gnu.org/licenses/gpl.html
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/gpl.html

View File

@@ -0,0 +1,182 @@
Description
------------------
Provides git commands on Side Bar of Files and Folders for Sublime Text 2. For sublime text see: http://www.sublimetext.com/
It should work with files names in any language.
This plugin is a port of the "Komodin" extension for komodo edit. See: https://github.com/titoBouzout/komodo-komodin-git
Usage
------------------
* Right click on "multiple/single" "files/folders" of the "tree" sidebar to apply commands on selected files which maybe are from different repositories.
* To apply commands to focused document use the document context menu.
* Tip: If a command sends output to a tab ( example a diff ), pressing F5 on that tab: will execute the command again and refresh the tab with the new contents ( example the new computed diff )
Provides the following commands
------------------
<pre>
Add & Commit
o git add -- "/selected/paths/files/or/and/folders"
o git commit -m "promptMessage" -- "/selected/paths/files/or/and/folders"
Add & Commit & Push
o git add -- "/selected/paths/files/or/and/folders"
o git commit -m "promptMessage" -- "/selected/paths/files/or/and/folders"
o git push
Commit
o git commit -m "promptMessage" -- "/selected/paths/files/or/and/folders"
Commit Undo
o git reset --soft HEAD~1
Commit Amend
o git commit --amend -C HEAD -- "/selected/paths/files/or/and/folders"
Commit All
o git commit -a -m "promptMessage"
Status
o git status --untracked-files=all -- "/selected/paths/files/or/and/folders"
Diff
all changes since the last commit
o git diff HEAD -- "/selected/paths/files/or/and/folders"
changes that have not been staged
o git diff -- "/selected/paths/files/or/and/folders"
changes that are staged but not committed
o git diff --staged -- "/selected/paths/files/or/and/folders"
between the index and last commit
o git diff --cached -- "/selected/paths/files/or/and/folders"
between remote and last local commit (origin/master..)
o git diff origin/master.. -- "/selected/paths/files/or/and/folders"
between last local commit and remote (..origin/master)
o git diff ..origin/master -- "/selected/paths/files/or/and/folders"
between the two latest tags
o git diff "previousTag".."lastTag" -- "/selected/paths/files/or/and/folders"
Log stat last 30
o git log -n 30 --stat --graph -- "/selected/paths/files/or/and/folders"
Log stat full
o git log --stat --graph -- "/selected/paths/files/or/and/folders"
Log extended last 30
o git log -n 30 -p -- "/selected/paths/files/or/and/folders"
Log extended full
o git log -p -- "/selected/paths/files/or/and/folders"
Log since last tag
o git log "lastTag"... --stat --graph -- "/selected/paths/files/or/and/folders"
Log since last push
o git log origin/master... --stat --graph -- "/selected/paths/files/or/and/folders"
Log between the two latest tags
o git log "prevToLastTag".."lastTag" --stat --graph -- "/selected/paths/files/or/and/folders"
Blame
o git blame -- "/selected/paths/files/NOT/folders"
Auto-Tag
o git tag "YYMMDD.Version"
Tag Add
o git tag "promptMessage"
Tag Remove
o git tag -d "promptMessage"
Tag List
o git tag -l
Revert Discard changes to tracked
o git checkout HEAD -- "/selected/paths/files/or/and/folders"
Revert Discard changes to tracked, clean untracked
o git checkout HEAD -- "/selected/paths/files/or/and/folders"
o git clean -f -d -- "/selected/paths/files/or/and/folders"
Revert Discard changes to tracked, clean untracked, unstage
o git checkout HEAD -- "/selected/paths/files/or/and/folders"
o git clean -f -d -- "/selected/paths/files/or/and/folders"
o git reset HEAD -- "/selected/paths/files/or/and/folders"
Revert Discard changes to tracked, unstage, clean untracked
o git checkout HEAD -- "/selected/paths/files/or/and/folders"
o git reset HEAD -- "/selected/paths/files/or/and/folders"
o git clean -f -d -- "/selected/paths/files/or/and/folders"
Revert Unstage
o git reset HEAD -- "/selected/paths/files/or/and/folders"
Checkout to
o git checkout promptMessage -- "/selected/paths/files/or/and/folders"
Checkout repo to
o cd repoPath
o git checkout promptMessage
Push
o git push
Push, Push Tags
o git push && git push --tags
Push Tags
o git push --tags
Push with options…
o promptMessage
Pull
o git pull
Pull with options…
o promptMessage
Fetch
o git fetch
Fetch with options…
o promptMessage
Remote add
o git remote add promptMessage
Configure default remote
o git config branch.promptBranch.remote promptRemoteName
Clone
o git clone promptMessage
Init
o git init
Add
o git add -- "/selected/paths/files/or/and/folders"
Remove
o git rm -r -f -- "/selected/paths/files/or/and/folders"
Remove Keep Local
o git rm -r --cached -- "/selected/paths/files/or/and/folders"
Open Git Ignore
Add to Git Ignore
Git GUI
Gitk
Liberal Git Command
</pre>
Installation
------------------
* Install this repository via "Package Control" http://wbond.net/sublime_packages/package_control
Todo
------------------
* Tag commands not yet ported
Source-code
------------------
https://github.com/SublimeText/SideBarGit
Forum Thread
------------------
http://www.sublimetext.com/forum/viewtopic.php?f=5&t=3405
Contribute
------------------
[Consider make a contribution](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=extensiondevelopment%40gmail%2ecom&lc=UY&item_name=Tito&item_number=sublime%2dtext%2dside%2dbar%2dplugin&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted )

View File

@@ -0,0 +1,317 @@
# coding=utf8
import sublime
import os
import subprocess
from SideBarItem import SideBarItem
class Object():
pass
s = sublime.load_settings('SideBarGit.sublime-settings')
path_to_git_unixes = s.get('path_to_git_unixes');
class SideBarGit:
last_stdout = ''
def run(
self,
object,
modal = False,
background = False,
refresh_funct_view = False,
refresh_funct_command = False,
refresh_funct_item = False,
refresh_funct_to_status_bar = False,
refresh_funct_title = False,
refresh_funct_no_results = False,
refresh_funct_syntax_file = False
):
if not refresh_funct_view:
pass
else:
object = Object()
object.command = refresh_funct_command
object.item = SideBarItem(refresh_funct_item, os.path.isdir(refresh_funct_item))
object.to_status_bar = refresh_funct_to_status_bar
object.title = refresh_funct_title
object.no_results = refresh_funct_no_results
object.syntax_file = refresh_funct_syntax_file
debug = False
if debug:
print '----------------------------------------------------------'
print 'GIT:'
print object.command
print 'CWD:'
print object.item.forCwdSystemPath()
print 'PATH:'
print object.item.forCwdSystemName()
failed = False
if sublime.platform() == 'windows':
object.command = map(self.escapeCMDWindows, object.command)
if sublime.platform() is not 'windows' and object.command[0] == 'git':
if path_to_git_unixes != '':
object.command[0] = s.get('path_to_git_unixes')
elif os.path.exists('/usr/local/git/bin'):
object.command[0] = '/usr/local/git/bin/git'
cwd = object.item.forCwdSystemPath()
try:
if sublime.platform() == 'windows':
process = subprocess.Popen(
#" ".join(object.command),
object.command,
cwd=cwd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
universal_newlines=True)
else:
process = subprocess.Popen(
object.command,
cwd=cwd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=False,
universal_newlines=True)
if background:
if debug:
print 'SUCCESS'
print '----------------------------------------------------------'
return True
stdout, stderr = process.communicate()
SideBarGit.last_stdout = str(stdout).rstrip()
self.last_stdout = str(stdout).rstrip()
stdout = stdout.strip()
if stdout.find('fatal:') == 0 or stdout.find('error:') == 0 or stdout.find('Permission denied') == 0 or stderr:
print 'FAILED'
failed = True
else:
if debug:
print 'SUCCESS'
if stdout:
if debug:
print 'STDOUT'
print stdout
if stderr:
print 'STDERR'
print stderr
except OSError as (errno, strerror):
print 'FAILED'
failed = True
print errno
print strerror
SideBarGit.last_stdout = ''
self.last_stdout = ''
except IOError as (errno, strerror):
print 'FAILED'
failed = True
print errno
print strerror
SideBarGit.last_stdout = ''
self.last_stdout = ''
if debug:
print '----------------------------------------------------------'
try:
object.to_status_bar
except:
object.to_status_bar = False
try:
object.silent
return
except:
pass
if failed:
try:
strerror
if errno == 2:
self.alert(strerror+'\nPossible error:\n'+object.command[0]+' not found on $PATH')
else:
self.alert(strerror)
return False
except:
if not stdout and not stderr:
return False
if stdout.find('Permission denied') == 0 or stdout.find('fatal: The remote end hung up unexpectedly') == 0:
self.alert((stdout or '')+'\n'+(stderr or '')+'\nPossible error:\nssh keys not in .ssh/ directory or keys not opened')
else:
self.alert((stdout or '')+'\n'+(stderr or ''))
return False
else:
if stdout != '' and refresh_funct_view == False and (object.to_status_bar or " ".join(object.command).find('git push') == 0 or stdout.find('nothing to commit') == 0):
self.status(stdout)
else:
if stdout == '' and refresh_funct_view == False:
try:
self.status(object.no_results)
except:
self.status('No output to show')
return True
if stdout == '' and refresh_funct_view != False:
try:
stdout = object.no_results
except:
stdout = 'No output to show'
if stdout == '':
return True
if refresh_funct_view == False:
view = sublime.active_window().new_file()
else:
view = refresh_funct_view
try:
view.set_name(object.title.decode('utf-8'))
except:
view.set_name('No Title')
try:
if object.syntax_file != False:
view.set_syntax_file(object.syntax_file)
except:
pass
try:
object.word_wrap
view.settings().set('word_wrap', False)
except:
pass
view.settings().set('fallback_encoding', 'UTF-8')
view.settings().set('encoding', 'UTF-8')
view.settings().set('default_dir', object.item.dirname())
view.set_scratch(True)
if refresh_funct_view == False:
view.settings().set('SideBarGitIsASideBarGitTab', True)
view.settings().set('SideBarGitCommand', object.command)
view.settings().set('SideBarGitModal', modal)
view.settings().set('SideBarGitBackground', background)
view.settings().set('SideBarGitItem', object.item.path())
try:
view.settings().set('SideBarGitToStatusBar', object.to_status_bar)
except:
view.settings().set('SideBarGitToStatusBar', False)
try:
view.settings().set('SideBarGitTitle', object.title)
except:
view.settings().set('SideBarGitTitle', 'No Title')
try:
view.settings().set('SideBarGitNoResults', object.no_results)
except:
view.settings().set('SideBarGitNoResults', 'No output to show')
try:
view.settings().set('SideBarGitSyntaxFile', object.syntax_file)
except:
view.settings().set('SideBarGitSyntaxFile', False)
content = "[SideBarGit@SublimeText "
content += object.item.name().decode('utf-8')
content += "/] "
content += (" ".join(object.command)).decode('utf-8')
content += "\n\n"
content += "# Improve this command, the output or the tab title by posting here:"
content += "\n"
content += "# http://www.sublimetext.com/forum/viewtopic.php?f=5&t=3405"
content += "\n"
content += "# Tip: F5 will run the command again and refresh the contents of this tab"
content += "\n\n"
try:
content += stdout
except:
content += unicode(stdout, 'UTF-8', errors='ignore')
edit = view.begin_edit()
view.replace(edit, sublime.Region(0, view.size()), content);
view.sel().clear()
view.sel().add(sublime.Region(0))
view.end_edit(edit)
return True
def confirm(self, message, function, arg1):
if int(sublime.version()) >= 2186:
if sublime.ok_cancel_dialog(u'Side Bar Git : '+message):
function(arg1, True)
else:
import functools
sublime.active_window().run_command('hide_panel');
sublime.active_window().show_input_panel("Confirmation Required:", message.decode('utf-8'), functools.partial(function, arg1, True), None, None)
def prompt(self, message, default, function, arg1):
import functools
sublime.active_window().run_command('hide_panel');
sublime.active_window().show_input_panel(message.decode('utf-8'), default.decode('utf-8'), functools.partial(function, arg1, True), None, None)
def alert(self, message):
try:
sublime.error_message('Git : '+(message.decode('utf-8')))
except:
try:
sublime.error_message('Git : '+message)
except:
print message
def status(self, message):
message = message[:200] + (message[200:] and '')
message = message.replace('\n', ' ')
try:
v = sublime.active_window().active_view()
v.set_status('SideBarGit', 'Git : '+(message.decode('utf-8')))
sublime.set_timeout(lambda: SideBarGit().statusRemove(v), 16000)
except:#there is no tabs opened
sublime.status_message('Git : '+(message.decode('utf-8')))
def statusRemove(self, v):
try:
v.erase_status('SideBarGit')
except:#this view is not there
pass
def quickPanel(self, function, extra, data):
import functools
window = sublime.active_window()
# window.show_input_panel("BUG!", '', '', None, None)
# window.run_command('hide_panel');
data = [item[:70] for item in data]
window.show_quick_panel(data, functools.partial(self.quickPanelDone, function, extra, data))
def quickPanelDone(self, function, extra, data, result):
if result != -1:
function(extra, data, result)
def getSelectedRepos(self, items):
repos = []
reposTemp = []
for item in items:
original = item.path()
while not os.path.exists(item.join('.git')):
if item.dirname() == item.path():
break;
item.path(item.dirname())
if os.path.exists(item.join('.git')):
try:
index = reposTemp.index(item.path())
except ValueError:
reposTemp.append(item.path())
index = reposTemp.index(item.path())
repos.append(Object())
repos[index].repository = item
repos[index].items = []
repos[index].items.append(SideBarItem(original, os.path.isdir(original)))
return repos
def escapeCMDWindows(self, string):
return string.replace('^', '^^')

View File

@@ -0,0 +1,480 @@
# coding=utf8
import sublime
import os
import re
import shutil
from SideBarProject import SideBarProject
try:
import desktop
except:
pass
class Object():
pass
def expand_vars(path):
for k, v in os.environ.iteritems():
try:
# dirty hack, this should be autofixed in python3
k = unicode(k.encode('utf8'))
v = unicode(v.encode('utf8'))
path = path.replace(u'%'+k+'%', v).replace(u'%'+k.lower()+'%', v)
except:
pass
return path
class SideBarItem:
def __init__(self, path, is_directory):
self._path = path
self._is_directory = is_directory
def path(self, path = ''):
if path == '':
return self._path
else:
self._path = path
self._is_directory = os.path.isdir(path)
return path
def pathSystem(self):
import sys
return self.path().encode(sys.getfilesystemencoding())
def pathWithoutProject(self):
path = self.path()
for directory in SideBarProject().getDirectories():
path = path.replace(directory, '', 1)
return path.replace('\\', '/')
def pathProject(self):
path = self.path()
for directory in SideBarProject().getDirectories():
path2 = path.replace(directory, '', 1)
if path2 != path:
return directory
return False
def projectURL(self, type):
filename = os.path.normpath(os.path.join(sublime.packages_path(), '..', 'Settings', 'SideBarEnhancements.json'))
if os.path.lexists(filename):
#try:
import json
data = file(filename, 'r').read()
data = data.replace('\t', ' ').replace('\\', '/').replace('\\', '/').replace('//', '/').replace('//', '/').replace('http:/', 'http://').replace('https:/', 'https://')
data = json.loads(data, strict=False)
for path in data.keys():
path2 = expand_vars(path)
print '-------------------------------------------------------'
print 'searching:'
path2 = path2.replace('\\', '/').replace('\\', '/').replace('//', '/').replace('//', '/')
print path2
print 'in:'
path3 = self.path().replace('\\', '/').replace('\\', '/').replace('//', '/').replace('//', '/')
print path3
print '-------------------------------------------------------'
path4 = re.sub(re.compile("^"+re.escape(path2), re.IGNORECASE), '', path3);
print path4
if path4 != path3:
url = data[path][type]
if url:
if url[-1:] != '/':
url = url+'/'
import urllib
return url+(re.sub("^/", '', urllib.quote(path4.encode('utf-8'))));
#except:
# return False
else:
return False
def isUnderCurrentProject(self):
path = self.path()
path2 = self.path()
for directory in SideBarProject().getDirectories():
path2 = path2.replace(directory, '', 1)
return path != path2
def pathRelativeFromProject(self):
return re.sub('^/+', '', self.pathWithoutProject())
def pathRelativeFromProjectEncoded(self):
import urllib
return urllib.quote(self.pathRelativeFromProject().encode('utf-8'))
def pathRelativeFromView(self):
return os.path.relpath(self.path(), os.path.dirname(sublime.active_window().active_view().file_name())).replace('\\', '/')
def pathRelativeFromViewEncoded(self):
import urllib
return urllib.quote(os.path.relpath(self.path(), os.path.dirname(sublime.active_window().active_view().file_name())).replace('\\', '/').encode('utf-8'))
def pathAbsoluteFromProject(self):
return self.pathWithoutProject()
def pathAbsoluteFromProjectEncoded(self):
import urllib
return urllib.quote(self.pathAbsoluteFromProject().encode('utf-8'))
def uri(self):
import urllib
return 'file:'+urllib.pathname2url(self.path().encode('utf-8'));
def join(self, name):
return os.path.join(self.path(), name)
def dirname(self):
branch, leaf = os.path.split(self.path())
return branch;
def forCwdSystemPath(self):
if self.isDirectory():
return self.pathSystem()
else:
return self.dirnameSystem()
def forCwdSystemName(self):
if self.isDirectory():
return '.'
else:
path = self.pathSystem()
branch = self.dirnameSystem()
leaf = path.replace(branch, '', 1).replace('\\', '').replace('/', '')
return leaf
def forCwdSystemPathRelativeFrom(self, relativeFrom):
relative = SideBarItem(relativeFrom, os.path.isdir(relativeFrom))
path = self.pathSystem().replace(relative.pathSystem(), '', 1).replace('\\', '/')
if path == '':
return '.'
else:
return re.sub('^/+', '', path)
def forCwdSystemPathRelativeFromRecursive(self, relativeFrom):
relative = SideBarItem(relativeFrom, os.path.isdir(relativeFrom))
path = self.pathSystem().replace(relative.pathSystem(), '', 1).replace('\\', '/')
if path == '':
return '.'
else:
if self.isDirectory():
return re.sub('^/+', '', path)+'/'
else:
return re.sub('^/+', '', path)
def dirnameSystem(self):
import sys
return self.dirname().encode(sys.getfilesystemencoding())
def dirnameCreate(self):
try:
os.makedirs(self.dirname())
except:
pass
def name(self):
branch, leaf = os.path.split(self.path())
return leaf;
def nameSystem(self):
import sys
return self.name().encode(sys.getfilesystemencoding())
def nameEncoded(self):
import urllib
return urllib.quote(self.name().encode('utf-8'));
def namePretty(self):
return self.name().replace(self.extension(), '').replace('-', ' ').replace('_', ' ').strip();
def open(self):
if sublime.platform() == 'osx':
import subprocess
subprocess.Popen(['open', '-a', self.nameSystem()], cwd=self.dirnameSystem())
elif sublime.platform() == 'windows':
import subprocess
subprocess.Popen([self.nameSystem()], cwd=self.dirnameSystem(), shell=True)
else:
desktop.open(self.path())
def edit(self):
return sublime.active_window().open_file(self.path())
def isDirectory(self):
return self._is_directory
def isFile(self):
return self.isDirectory() == False
def contentUTF8(self):
import codecs
return codecs.open(self.path(), 'r', 'utf-8').read()
def contentBinary(self):
return file(self.path(), "rb").read()
def contentBase64(self):
return 'data:'+self.mime()+';base64,'+(file(self.path(), "rb").read().encode("base64").replace('\n', ''))
def reveal(self):
sublime.active_window().run_command("open_dir", {"dir": self.dirname(), "file": self.name()} )
def write(self, content):
file(self.path(), 'w+').write(content)
def mime(self):
import mimetypes
return mimetypes.guess_type(self.path())[0] or 'application/octet-stream'
def extension(self):
return os.path.splitext('name'+self.name())[1].lower()
def exists(self):
return os.path.isdir(self.path()) or os.path.isfile(self.path())
def create(self):
if self.isDirectory():
self.dirnameCreate()
os.makedirs(self.path())
else:
self.dirnameCreate()
self.write('')
def copy(self, location, replace = False):
location = SideBarItem(location, os.path.isdir(location));
if location.exists() and replace == False:
return False
elif location.exists() and location.isFile():
os.remove(location.path())
location.dirnameCreate();
if self.isDirectory():
if location.exists():
self.copy_recursive(self.path(), location.path())
else:
shutil.copytree(self.path(), location.path())
else:
shutil.copy2(self.path(), location.path())
return True
def copy_recursive(self, _from, _to):
if os.path.isfile(_from) or os.path.islink(_from):
try:
os.makedirs(os.path.dirname(_to));
except:
pass
if os.path.exists(_to):
os.remove(_to)
shutil.copy2(_from, _to)
else:
try:
os.makedirs(_to);
except:
pass
for content in os.listdir(_from):
__from = os.path.join(_from, content)
__to = os.path.join(_to, content)
self.copy_recursive(__from, __to)
def move(self, location, replace = False):
location = SideBarItem(location, os.path.isdir(location));
if location.exists() and replace == False:
if self.path().lower() == location.path().lower():
pass
else:
return False
elif location.exists() and location.isFile():
os.remove(location.path())
if self.path().lower() == location.path().lower():
location.dirnameCreate();
os.rename(self.path(), location.path()+'.sublime-temp')
os.rename(location.path()+'.sublime-temp', location.path())
self._move_moveViews(self.path(), location.path())
else:
location.dirnameCreate();
if location.exists():
self.move_recursive(self.path(), location.path())
else:
os.rename(self.path(), location.path())
self._move_moveViews(self.path(), location.path())
return True
def move_recursive(self, _from, _to):
if os.path.isfile(_from) or os.path.islink(_from):
try:
os.makedirs(os.path.dirname(_to));
except:
pass
if os.path.exists(_to):
os.remove(_to)
os.rename(_from, _to)
else:
try:
os.makedirs(_to);
except:
pass
for content in os.listdir(_from):
__from = os.path.join(_from, content)
__to = os.path.join(_to, content)
self.move_recursive(__from, __to)
os.rmdir(_from)
def _move_moveViews(self, old, location):
for window in sublime.windows():
active_view = window.active_view()
views = []
for view in window.views():
if view.file_name():
views.append(view)
views.reverse();
for view in views:
if old == view.file_name():
active_view = self._move_moveView(window, view, location, active_view)
elif view.file_name().find(old+'\\') == 0:
active_view = self._move_moveView(window, view, view.file_name().replace(old+'\\', location+'\\', 1), active_view)
elif view.file_name().find(old+'/') == 0:
active_view = self._move_moveView(window, view, view.file_name().replace(old+'/', location+'/', 1), active_view)
def _move_moveView(self, window, view, location, active_view):
if active_view == view:
is_active_view = True
else:
is_active_view = False
options = Object()
options.scroll = view.viewport_position()
options.selections = [[item.a, item.b] for item in view.sel()]
options.marks = [[item.a, item.b] for item in view.get_regions("mark")]
options.bookmarks = [[item.a, item.b] for item in view.get_regions("bookmarks")]
if int(sublime.version()) >= 2167:
options.folds = [[item.a, item.b] for item in view.folded_regions()]
else:
options.folds = [[item.a, item.b] for item in view.unfold(sublime.Region(0, view.size()))]
options.syntax = view.settings().get('syntax')
try:
_window = window or view.window() or sublime.active_window()
options.position = _window.get_view_index(view)
except:
options.position = False
window.focus_view(view)
if view.is_dirty():
options.content = view.substr(sublime.Region(0, view.size()))
view.window().run_command('revert')
else:
options.content = False
_view = view
view = window.open_file(location)
window.focus_view(_view)
window.run_command('close')
sublime.set_timeout(lambda: self._move_restoreView(view, options, window), 200)
if is_active_view:
window.focus_view(view)
return view
else:
window.focus_view(active_view)
return active_view
def _move_restoreView(self, view, options, window):
if view.is_loading():
sublime.set_timeout(lambda: self._move_restoreView(view, options, window), 100)
else:
if options.content != False:
edit = view.begin_edit()
view.replace(edit, sublime.Region(0, view.size()), options.content);
view.sel().clear()
view.sel().add(sublime.Region(0))
view.end_edit(edit)
if options.position != False:
try:
_window = window or view.window() or sublime.active_window()
group, index = options.position
_window.set_view_index(view, group, index)
except:
pass
if options.syntax:
view.settings().set('syntax', options.syntax);
for r in options.folds:
view.fold(sublime.Region(r[0], r[1]))
view.sel().clear()
for r in options.selections:
view.sel().add(sublime.Region(r[0], r[1]))
rs = []
for r in options.marks:
rs.append(sublime.Region(r[0], r[1]))
if len(rs):
view.add_regions("mark", rs, "mark", "dot", sublime.HIDDEN | sublime.PERSISTENT)
rs = []
for r in options.bookmarks:
rs.append(sublime.Region(r[0], r[1]))
if len(rs):
view.add_regions("bookmarks", rs, "bookmarks", "bookmark", sublime.HIDDEN | sublime.PERSISTENT)
view.set_viewport_position(options.scroll, False)
def close_associated_buffers(self):
path = self.path()
closed_items = []
for window in sublime.windows():
active_view = window.active_view()
views = []
for view in window.views():
if view.file_name():
views.append(view)
views.reverse();
for view in views:
if path == view.file_name():
if view.window():
closed_items.append([view.file_name(), view.window(), view.window().get_view_index(view)])
if len(window.views()) == 1:
window.new_file()
window.focus_view(view)
window.run_command('revert')
window.run_command('close')
elif view.file_name().find(path+'\\') == 0:
if view.window():
closed_items.append([view.file_name(), view.window(), view.window().get_view_index(view)])
if len(window.views()) == 1:
window.new_file()
window.focus_view(view)
window.run_command('revert')
window.run_command('close')
elif view.file_name().find(path+'/') == 0:
if view.window():
closed_items.append([view.file_name(), view.window(), view.window().get_view_index(view)])
if len(window.views()) == 1:
window.new_file()
window.focus_view(view)
window.run_command('revert')
window.run_command('close')
# try to repaint
try:
window.focus_view(active_view)
window.focus_view(window.active_view())
except:
try:
window.focus_view(window.active_view())
except:
pass
return closed_items

View File

@@ -0,0 +1,119 @@
import sublime
import re
import os
class SideBarProject:
def getDirectories(self):
return sublime.active_window().folders()
def hasOpenedProject(self):
return self.getProjectFile() != None
def getDirectoryFromPath(self, path):
for directory in self.getDirectories():
maybe_path = path.replace(directory, '', 1)
if maybe_path != path:
return directory
def getProjectFile(self):
if not self.getDirectories():
return None
import json
data = file(os.path.normpath(os.path.join(sublime.packages_path(), '..', 'Settings', 'Session.sublime_session')), 'r').read()
data = data.replace('\t', ' ')
data = json.loads(data, strict=False)
projects = data['workspaces']['recent_workspaces']
if os.path.lexists(os.path.join(sublime.packages_path(), '..', 'Settings', 'Auto Save Session.sublime_session')):
data = file(os.path.normpath(os.path.join(sublime.packages_path(), '..', 'Settings', 'Auto Save Session.sublime_session')), 'r').read()
data = data.replace('\t', ' ')
data = json.loads(data, strict=False)
if 'workspaces' in data and 'recent_workspaces' in data['workspaces'] and data['workspaces']['recent_workspaces']:
projects += data['workspaces']['recent_workspaces']
projects = list(set(projects))
for project_file in projects:
project_file = re.sub(r'^/([^/])/', '\\1:/', project_file);
project_json = json.loads(file(project_file, 'r').read(), strict=False)
if 'folders' in project_json:
folders = project_json['folders']
found_all = True
for directory in self.getDirectories():
found = False
for folder in folders:
folder_path = re.sub(r'^/([^/])/', '\\1:/', folder['path']);
if folder_path == directory.replace('\\', '/'):
found = True
break;
if found == False:
found_all = False
break;
if found_all:
return project_file
return None
def getProjectJson(self):
if not self.hasOpenedProject():
return None
import json
return json.loads(file(self.getProjectFile(), 'r').read(), strict=False)
def excludeDirectory(self, path):
import json
project_file = self.getProjectFile();
project = self.getProjectJson()
path = re.sub(r'^([^/])\:/', '/\\1/', path.replace('\\', '/'))
for folder in project['folders']:
if path.find(folder['path']) == 0:
try:
folder['folder_exclude_patterns'].append(re.sub(r'/+$', '', path.replace(folder['path']+'/', '', 1)))
except:
folder['folder_exclude_patterns'] = [re.sub(r'/+$', '', path.replace(folder['path']+'/', '', 1))]
file(project_file, 'w+').write(json.dumps(project, indent=1))
return
def excludeFile(self, path):
import json
project_file = self.getProjectFile();
project = self.getProjectJson()
path = re.sub(r'^([^/])\:/', '/\\1/', path.replace('\\', '/'))
for folder in project['folders']:
if path.find(folder['path']) == 0:
try:
folder['file_exclude_patterns'].append(path.replace(folder['path']+'/', '', 1))
except:
folder['file_exclude_patterns'] = [path.replace(folder['path']+'/', '', 1)]
file(project_file, 'w+').write(json.dumps(project, indent=1))
return
def rootAdd(self, path):
import json
project_file = self.getProjectFile();
project = self.getProjectJson()
path = re.sub(r'^([^/])\:/', '/\\1/', path.replace('\\', '/'))
project['folders'].append({'path':path});
file(project_file, 'w+').write(json.dumps(project, indent=1))
def refresh(self):
try:
sublime.set_timeout(lambda:sublime.active_window().run_command('refresh_folder_list'), 200);
sublime.set_timeout(lambda:sublime.active_window().run_command('refresh_folder_list'), 600);
sublime.set_timeout(lambda:sublime.active_window().run_command('refresh_folder_list'), 1300);
sublime.set_timeout(lambda:sublime.active_window().run_command('refresh_folder_list'), 2300);
except:
pass
def getPreference(self, name):
if not self.hasOpenedProject():
return None
project = self.getProjectJson()
try:
return project[name]
except:
return None

View File

@@ -0,0 +1,186 @@
# coding=utf8
import sublime
import os
import re
from SideBarProject import SideBarProject
from SideBarItem import SideBarItem
class SideBarSelection:
def __init__(self, paths = []):
if len(paths) < 1:
try:
path = sublime.active_window().active_view().file_name()
if self.isNone(path):
paths = []
else:
paths = [path]
except:
paths = []
self._paths = paths
self._paths.sort()
self._obtained_selection_information_basic = False
self._obtained_selection_information_extended = False
def len(self):
return len(self._paths)
def hasDirectories(self):
self._obtainSelectionInformationBasic()
return self._has_directories
def hasFiles(self):
self._obtainSelectionInformationBasic()
return self._has_files
def hasOnlyDirectories(self):
self._obtainSelectionInformationBasic()
return self._only_directories
def hasOnlyFiles(self):
self._obtainSelectionInformationBasic()
return self._only_files
def hasProjectDirectories(self):
if self.hasDirectories():
project_directories = SideBarProject().getDirectories()
for item in self.getSelectedDirectories():
if item.path() in project_directories:
return True
return False
else:
return False
def hasItemsUnderProject(self):
for item in self.getSelectedItems():
if item.isUnderCurrentProject():
return True
return False
def hasImages(self):
return self.hasFilesWithExtension('gif|jpg|jpeg|png')
def hasFilesWithExtension(self, extensions):
extensions = re.compile('('+extensions+')$', re.I);
for item in self.getSelectedFiles():
if extensions.search(item.path()):
return True;
return False
def getSelectedItems(self):
self._obtainSelectionInformationExtended()
return self._files + self._directories;
def getSelectedItemsWithoutChildItems(self):
self._obtainSelectionInformationExtended()
items = []
for item in self._items_without_containing_child_items:
items.append(SideBarItem(item, os.path.isdir(item)))
return items
def getSelectedDirectories(self):
self._obtainSelectionInformationExtended()
return self._directories;
def getSelectedFiles(self):
self._obtainSelectionInformationExtended()
return self._files;
def getSelectedDirectoriesOrDirnames(self):
self._obtainSelectionInformationExtended()
return self._directories_or_dirnames;
def getSelectedImages(self):
return self.getSelectedFilesWithExtension('gif|jpg|jpeg|png')
def getSelectedFilesWithExtension(self, extensions):
items = []
extensions = re.compile('('+extensions+')$', re.I);
for item in self.getSelectedFiles():
if extensions.search(item.path()):
items.append(item)
return items
def _obtainSelectionInformationBasic(self):
if not self._obtained_selection_information_basic:
self._obtained_selection_information_basic = True
self._has_directories = False
self._has_files = False
self._only_directories = False
self._only_files = False
for path in self._paths:
if self._has_directories == False and os.path.isdir(path):
self._has_directories = True
if self._has_files == False and os.path.isdir(path) == False:
self._has_files = True
if self._has_files and self._has_directories:
break
if self._has_files and self._has_directories:
self._only_directories = False
self._only_files = False
elif self._has_files:
self._only_files = True
elif self._has_directories:
self._only_directories = True
def _obtainSelectionInformationExtended(self):
if not self._obtained_selection_information_extended:
self._obtained_selection_information_extended = True
self._directories = []
self._files = []
self._directories_or_dirnames = []
self._items_without_containing_child_items = []
_directories = []
_files = []
_directories_or_dirnames = []
_items_without_containing_child_items = []
for path in self._paths:
if os.path.isdir(path):
item = SideBarItem(path, True)
if item.path() not in _directories:
_directories.append(item.path())
self._directories.append(item)
if item.path() not in _directories_or_dirnames:
_directories_or_dirnames.append(item.path())
self._directories_or_dirnames.append(item)
_items_without_containing_child_items = self._itemsWithoutContainingChildItems(_items_without_containing_child_items, item.path())
else:
item = SideBarItem(path, False)
if item.path() not in _files:
_files.append(item.path())
self._files.append(item)
_items_without_containing_child_items = self._itemsWithoutContainingChildItems(_items_without_containing_child_items, item.path())
item = SideBarItem(os.path.dirname(path), True)
if item.path() not in _directories_or_dirnames:
_directories_or_dirnames.append(item.path())
self._directories_or_dirnames.append(item)
self._items_without_containing_child_items = _items_without_containing_child_items
def _itemsWithoutContainingChildItems(self, items, item):
new_list = []
add = True
for i in items:
if i.find(item+'\\') == 0 or i.find(item+'/') == 0:
continue
else:
new_list.append(i)
if (item+'\\').find(i+'\\') == 0 or (item+'/').find(i+'/') == 0:
add = False
if add:
new_list.append(item)
return new_list
def isNone(self, path):
if path == None or path == '' or path == '.' or path == '..' or path == './' or path == '/' or path == '//' or path == '\\' or path == '\\\\' or path == '\\\\\\\\':
return True
else:
return False