Skip to content

Commit c7e735d

Browse files
Improve compatibility
* Add Windows support. Resolves #1 * Use sublime.set_timeout instead of a background thread, to avoid RuntimeError: Must call on main thread, consider using sublime.set_timeout(function, timeout) when trying to access settings on Sublime 2 on Windows and perhaps some other platforms too.
1 parent 0bba268 commit c7e735d

File tree

1 file changed

+66
-23
lines changed

1 file changed

+66
-23
lines changed

gitignore_plugin.py

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,37 @@
33
import os
44
import os.path
55
import threading
6+
import platform
67
from time import sleep
78

89
# Used for output suppression when calling subprocess functions; see
910
# http://stackoverflow.com/questions/10251391/suppressing-output-in-python-subprocess-call
1011
devnull = open(os.devnull, 'w')
1112

13+
# Used to prevent a new command prompt window from popping up every time a new
14+
# process is spawned on Windows. See
15+
# https://docs.python.org/2/library/subprocess.html#subprocess.STARTUPINFO
16+
if platform.system() == 'Windows':
17+
startupinfo = subprocess.STARTUPINFO()
18+
startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW
19+
startupinfo.wShowWindow = subprocess.SW_HIDE
20+
else:
21+
startupinfo = None
22+
1223
def start(): # Gets invoked at the bottom of this file.
1324
"""
1425
Regularly (every 5s) updates the file_exclude_patterns setting from a
1526
background thread.
16-
"""
27+
"""
1728
if is_first_launch():
1829
migrate_exclude_patterns()
1930
record_first_launch()
2031

2132
def run():
22-
while True:
23-
update_file_exclude_patterns()
24-
sleep(5)
25-
26-
thread = threading.Thread(target=run)
27-
thread.daemon = True
28-
thread.start()
33+
update_file_exclude_patterns()
34+
sublime.set_timeout(run, 5000)
35+
36+
run()
2937

3038
def update_file_exclude_patterns():
3139
"""
@@ -39,11 +47,22 @@ def update_file_exclude_patterns():
3947
file_exclude_patterns = s.get('extra_file_exclude_patterns', [])
4048
folder_exclude_patterns = s.get('extra_folder_exclude_patterns', [])
4149
for path in all_ignored_paths():
42-
if os.path.isdir(path):
43-
folder_exclude_patterns.append(path.rstrip(u'/'))
50+
is_directory = os.path.isdir(path)
51+
if platform.system() == 'Windows':
52+
# For some bizarre reason Sublime wants all its filenames to look like
53+
# C/somedir/somefile
54+
# instead of
55+
# C:\somedir\somefile
56+
# as they are normally written on Windows, and will not understand the
57+
# latter at all. All the other functions in this file return paths with
58+
# OS-standard separtors and include the colon after the drive letter on
59+
# Windows, so we need to convert them here to Sublime-format.
60+
path = windows_path_to_sublime_path(path)
61+
if is_directory:
62+
folder_exclude_patterns.append(path)
4463
else:
4564
file_exclude_patterns.append(path)
46-
65+
4766
new_files = set(file_exclude_patterns)
4867
old_files = set(s.get('file_exclude_patterns', []))
4968
new_folders = set(folder_exclude_patterns)
@@ -52,8 +71,8 @@ def update_file_exclude_patterns():
5271
# Only make changes if anything has actually changed, to avoid spamming the
5372
# sublime console
5473
if new_files != old_files or new_folders != old_folders:
55-
s.set('file_exclude_patterns', file_exclude_patterns)
56-
s.set('folder_exclude_patterns', folder_exclude_patterns)
74+
s.set('file_exclude_patterns', list(file_exclude_patterns))
75+
s.set('folder_exclude_patterns', list(folder_exclude_patterns))
5776
sublime.save_settings("Preferences.sublime-settings")
5877

5978
def all_ignored_paths():
@@ -91,7 +110,7 @@ def folder_ignored_paths(folder):
91110
# find the .git folder of the repo containing it:
92111
if is_in_git_repo(folder):
93112
repos.add(parent_repo_path(folder))
94-
113+
95114
# Now we find all the ignored paths in any of the above repos
96115
for git_repo in repos:
97116
ignored_paths = repo_ignored_paths(git_repo)
@@ -108,7 +127,8 @@ def is_in_git_repo(folder):
108127
["git", "rev-parse", "--is-inside-work-tree"],
109128
cwd=folder,
110129
stdout=devnull,
111-
stderr=devnull
130+
stderr=devnull,
131+
startupinfo=startupinfo
112132
)
113133

114134
return exit_code == 0
@@ -119,11 +139,17 @@ def parent_repo_path(folder):
119139
parent repo.
120140
"""
121141

122-
return subprocess.Popen(
123-
['git', 'rev-parse', '--show-toplevel'],
124-
stdout=subprocess.PIPE,
125-
cwd=folder
126-
).stdout.read().decode('utf-8', 'ignore').strip()
142+
# abspath call converts forward slashes to backslashes on Windows; we do
143+
# this wherever necessary to keep the format of our paths standardised on
144+
# Windows.
145+
return os.path.abspath(
146+
subprocess.Popen(
147+
['git', 'rev-parse', '--show-toplevel'],
148+
stdout=subprocess.PIPE,
149+
cwd=folder,
150+
startupinfo=startupinfo
151+
).stdout.read().decode('utf-8', 'ignore').strip()
152+
)
127153

128154
def find_git_repos(folder):
129155
"""
@@ -145,7 +171,8 @@ def repo_ignored_paths(git_repo):
145171
command_output = subprocess.Popen(
146172
['git', 'clean', '-ndX'],
147173
stdout=subprocess.PIPE,
148-
cwd=git_repo
174+
cwd=git_repo,
175+
startupinfo=startupinfo
149176
).stdout.read()
150177

151178
command_output = command_output.decode('utf-8', 'ignore')
@@ -157,8 +184,9 @@ def repo_ignored_paths(git_repo):
157184
# Each line in `lines` now looks something like:
158185
# "Would remove foo/bar/yourfile.txt"
159186

160-
relative_paths = [line.replace(u'Would remove ', u'', 1) for line in lines]
161-
absolute_paths = [git_repo + u'/' + path for path in relative_paths]
187+
relative_paths = [line.replace(u'Would remove ', u'', 1).rstrip(u'/')
188+
for line in lines]
189+
absolute_paths = [os.path.join(git_repo, path) for path in relative_paths]
162190

163191
return absolute_paths
164192

@@ -182,5 +210,20 @@ def record_first_launch():
182210
s = sublime.load_settings("gitignorer.sublime-settings")
183211
s.set('_sublime_gitignorer_has_run', True)
184212
sublime.save_settings("gitignorer.sublime-settings")
213+
214+
def windows_path_to_sublime_path(path):
215+
"""
216+
Removes the colon after the drive letter and replaces backslashes with
217+
slashes.
218+
219+
e.g.
220+
221+
windows_path_to_sublime_path("C:\somedir\somefile")
222+
== "C/somedir/somefile"
223+
"""
224+
225+
assert(path[1] == u':')
226+
without_colon = path[0] + path[2:]
227+
return without_colon.replace(u'\\', u'/')
185228

186229
start()

0 commit comments

Comments
 (0)