33import os
44import os .path
55import threading
6+ import platform
67from 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
1011devnull = 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+
1223def 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
3038def 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
5978def 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
128154def 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
186229start ()
0 commit comments