Updated default colors for pyuic4/po2qm build objects
[ugit.git] / ugitlibs / utils.py
blob2cb3de2e9eb0e4fe1fd4043a225883fe1da0e89b
1 #!/usr/bin/env python
2 import os
3 import re
4 import time
5 import commands
6 import defaults
7 from cStringIO import StringIO
9 KNOWN_FILE_TYPES = {
10 'ascii c': 'c.png',
11 'python': 'script.png',
12 'ruby': 'script.png',
13 'shell': 'script.png',
14 'perl': 'script.png',
15 'java': 'script.png',
16 'assembler': 'binary.png',
17 'binary': 'binary.png',
18 'byte': 'binary.png',
19 'image': 'image.png',
22 ICONSDIR = os.path.join(os.path.dirname(__file__), 'icons')
24 def ident_file_type(filename):
25 '''Returns an icon based on the contents of filename.'''
26 if os.path.exists(filename):
27 quoted_filename = shell_quote(filename)
28 fileinfo = commands.getoutput('file -b %s' % quoted_filename)
29 for filetype, iconname in KNOWN_FILE_TYPES.iteritems():
30 if filetype in fileinfo.lower():
31 return iconname
32 else:
33 return 'removed.png'
34 # Fallback for modified files of an unknown type
35 return 'generic.png'
37 def get_icon(filename):
38 '''Returns the full path to an icon file corresponding to
39 filename's contents.'''
40 icon_file = ident_file_type(filename)
41 return os.path.join(ICONSDIR, icon_file)
43 def get_staged_icon(filename):
44 '''Special-case method for staged items. These are only
45 ever 'staged' and 'removed' items in the staged list.'''
47 if os.path.exists(filename):
48 return os.path.join(ICONSDIR, 'staged.png')
49 else:
50 return os.path.join(ICONSDIR, 'removed.png')
52 def get_untracked_icon():
53 return os.path.join(ICONSDIR, 'untracked.png')
55 def get_directory_icon():
56 return os.path.join(ICONSDIR, 'dir.png')
58 def get_file_icon():
59 return os.path.join(ICONSDIR, 'generic.png')
61 def grep(pattern, items, squash=True):
62 regex = re.compile(pattern)
63 matched = []
64 for item in items:
65 match = regex.match(item)
66 if not match: continue
67 groups = match.groups()
68 if not groups:
69 subitems = match.group(0)
70 else:
71 if len(groups) == 1:
72 subitems = groups[0]
73 else:
74 subitems = list(groups)
75 matched.append(subitems)
77 if squash and len(matched) == 1:
78 return matched[0]
79 else:
80 return matched
82 def basename(path):
83 '''Avoid os.path.basename because we are explicitly
84 parsing git's output, which contains /'s regardless
85 of platform (a.t.m.)
86 '''
87 base_regex = re.compile('(.*?/)?([^/]+)$')
88 match = base_regex.match(path)
89 if match:
90 return match.group(2)
91 else:
92 return pathstr
94 def shell_quote(*inputs):
95 '''Quote strings so that they can be suitably martialled
96 off to the shell. This method supports POSIX sh syntax.
97 This is crucial to properly handle command line arguments
98 with spaces, quotes, double-quotes, etc.'''
100 regex = re.compile('[^\w!%+,\-./:@^]')
101 quote_regex = re.compile("((?:'\\''){2,})")
103 ret = []
104 for input in inputs:
105 if not input:
106 continue
108 if '\x00' in input:
109 raise AssertionError,('No way to quote strings '
110 'containing null(\\000) bytes')
112 # = does need quoting else in command position it's a
113 # program-local environment setting
114 match = regex.search(input)
115 if match and '=' not in input:
116 # ' -> '\''
117 input = input.replace("'", "'\\''")
119 # make multiple ' in a row look simpler
120 # '\'''\'''\'' -> '"'''"'
121 quote_match = quote_regex.match(input)
122 if quote_match:
123 quotes = match.group(1)
124 input.replace(quotes,
125 ("'" *(len(quotes)/4)) + "\"'")
127 input = "'%s'" % input
128 if input.startswith("''"):
129 input = input[2:]
131 if input.endswith("''"):
132 input = input[:-2]
133 ret.append(input)
134 return ' '.join(ret)
136 def get_tmp_filename():
137 # Allow TMPDIR/TMP with a fallback to /tmp
138 return '.ugit.%s.%s' %( os.getpid(), time.time() )
140 HEADER_LENGTH = 80
141 def header(msg):
142 pad = HEADER_LENGTH - len(msg) - 4 # len(':+') + len('+:')
143 extra = pad % 2
144 pad /= 2
145 return(':+'
146 +(' ' * pad)
147 + msg
148 +(' ' *(pad + extra))
149 + '+:'
150 + '\n')
152 def parse_geom(geomstr):
153 regex = re.compile('^(\d+)x(\d+)\+(\d+),(\d+) (\d+),(\d+) (\d+),(\d+)')
154 match = regex.match(geomstr)
155 if match:
156 defaults.WIDTH = int(match.group(1))
157 defaults.HEIGHT = int(match.group(2))
158 defaults.X = int(match.group(3))
159 defaults.Y = int(match.group(4))
160 defaults.SPLITTER_TOP_0 = int(match.group(5))
161 defaults.SPLITTER_TOP_1 = int(match.group(6))
162 defaults.SPLITTER_BOTTOM_0 = int(match.group(7))
163 defaults.SPLITTER_BOTTOM_1 = int(match.group(8))
165 return (defaults.WIDTH, defaults.HEIGHT,
166 defaults.X, defaults.Y,
167 defaults.SPLITTER_TOP_0, defaults.SPLITTER_TOP_1,
168 defaults.SPLITTER_BOTTOM_0, defaults.SPLITTER_BOTTOM_1)
170 def get_geom():
171 return '%dx%d+%d,%d %d,%d %d,%d' % (
172 defaults.WIDTH, defaults.HEIGHT,
173 defaults.X, defaults.Y,
174 defaults.SPLITTER_TOP_0, defaults.SPLITTER_TOP_1,
175 defaults.SPLITTER_BOTTOM_0, defaults.SPLITTER_BOTTOM_1)
177 def project_name():
178 return os.path.basename(defaults.DIRECTORY)
180 def slurp(path):
181 file = open(path)
182 slushy = file.read()
183 file.close()
184 return slushy
186 def write(path, contents):
187 file = open(path, 'w')
188 file.write(contents)
189 file.close()
192 class DiffParser(object):
193 def __init__(self, diff):
194 self.__diff_header = re.compile('^@@\s[^@]+\s@@.*')
196 self.__idx = -1
197 self.__diffs = []
198 self.__diff_spans = []
199 self.__diff_offsets = []
201 self.parse_diff(diff)
203 def get_diffs(self):
204 return self.__diffs
206 def get_spans(self):
207 return self.__diff_spans
209 def get_offsets(self):
210 return self.__diff_offsets
212 def get_diff_for_offset(self, offset):
213 for idx, diff_offset in enumerate(self.__diff_offsets):
214 if offset < diff_offset:
215 return os.linesep.join(self.__diffs[idx])
216 return None
218 def get_diffs_for_range(self, start, end):
219 diffs = []
220 for idx, span in enumerate(self.__diff_spans):
222 has_end_of_diff = start >= span[0] and start < span[1]
223 has_all_of_diff = start <= span[0] and end >= span[1]
224 has_head_of_diff = end >= span[0] and end <= span[1]
226 selected_diff =(has_end_of_diff
227 or has_all_of_diff
228 or has_head_of_diff)
230 if selected_diff:
231 diff = os.linesep.join(self.__diffs[idx])
232 diffs.append(diff)
235 return diffs
237 def parse_diff(self, diff):
238 total_offset = 0
239 for idx, line in enumerate(diff.splitlines()):
241 if self.__diff_header.match(line):
242 self.__diffs.append( [line] )
244 line_len = len(line) + 1
245 self.__diff_spans.append([total_offset,
246 total_offset + line_len])
248 total_offset += line_len
249 self.__diff_offsets.append(total_offset)
251 self.__idx += 1
252 else:
253 if self.__idx < 0:
254 errmsg = 'Malformed diff?\n\n%s' % diff
255 raise AssertionError, errmsg
257 line_len = len(line) + 1
258 total_offset += line_len
260 self.__diffs[self.__idx].append(line)
261 self.__diff_spans[-1][-1] += line_len
262 self.__diff_offsets[self.__idx] += line_len