6 from cStringIO
import StringIO
10 'python': 'script.png',
12 'shell': 'script.png',
15 'assembler': 'binary.png',
16 'binary': 'binary.png',
21 ICONSDIR
= os
.path
.join(os
.path
.dirname(__file__
), 'icons')
23 def ident_file_type(filename
):
24 '''Returns an icon based on the contents of filename.'''
25 if os
.path
.exists(filename
):
26 quoted_filename
= shell_quote(filename
)
27 fileinfo
= commands
.getoutput('file -b %s' % quoted_filename
)
28 for filetype
, iconname
in KNOWN_FILE_TYPES
.iteritems():
29 if filetype
in fileinfo
.lower():
33 # Fallback for modified files of an unknown type
36 def get_icon(filename
):
37 '''Returns the full path to an icon file corresponding to
38 filename's contents.'''
39 icon_file
= ident_file_type(filename
)
40 return os
.path
.join(ICONSDIR
, icon_file
)
42 def get_staged_icon(filename
):
43 '''Special-case method for staged items. These are only
44 ever 'staged' and 'removed' items in the staged list.'''
46 if os
.path
.exists(filename
):
47 return os
.path
.join(ICONSDIR
, 'staged.png')
49 return os
.path
.join(ICONSDIR
, 'removed.png')
51 def get_untracked_icon():
52 return os
.path
.join(ICONSDIR
, 'untracked.png')
54 def get_directory_icon():
55 return os
.path
.join(ICONSDIR
, 'dir.png')
58 return os
.path
.join(ICONSDIR
, 'generic.png')
60 def shell_quote(*inputs
):
61 '''Quote strings so that they can be suitably martialled
62 off to the shell. This method supports POSIX sh syntax.
63 This is crucial to properly handle command line arguments
64 with spaces, quotes, double-quotes, etc.'''
66 regex
= re
.compile('[^\w!%+,\-./:@^]')
67 quote_regex
= re
.compile("((?:'\\''){2,})")
75 raise AssertionError,('No way to quote strings '
76 'containing null(\\000) bytes')
78 # = does need quoting else in command position it's a
79 # program-local environment setting
80 match
= regex
.search(input)
81 if match
and '=' not in input:
83 input = input.replace("'", "'\\''")
85 # make multiple ' in a row look simpler
86 # '\'''\'''\'' -> '"'''"'
87 quote_match
= quote_regex
.match(input)
89 quotes
= match
.group(1)
91 ("'" *(len(quotes
)/4)) + "\"'")
93 input = "'%s'" % input
94 if input.startswith("''"):
97 if input.endswith("''"):
102 def get_tmp_filename():
103 # Allow TMPDIR/TMP with a fallback to /tmp
104 return '.ugit.%s.%s' %( os
.getpid(), time
.time() )
108 pad
= HEADER_LENGTH
- len(msg
) - 4 # len(':+') + len('+:')
114 +(' ' *(pad
+ extra
))
124 def write(path
, contents
):
125 file = open(path
, 'w')
130 class DiffParser(object):
131 def __init__(self
, diff
):
132 self
.__diff
_header
= re
.compile('^@@\s[^@]+\s@@.*')
136 self
.__diff
_spans
= []
137 self
.__diff
_offsets
= []
139 self
.parse_diff(diff
)
145 return self
.__diff
_spans
147 def get_offsets(self
):
148 return self
.__diff
_offsets
150 def get_diff_for_offset(self
, offset
):
151 for idx
, diff_offset
in enumerate(self
.__diff
_offsets
):
152 if offset
< diff_offset
:
153 return os
.linesep
.join(self
.__diffs
[idx
])
156 def get_diffs_for_range(self
, start
, end
):
158 for idx
, span
in enumerate(self
.__diff
_spans
):
160 has_end_of_diff
= start
>= span
[0] and start
< span
[1]
161 has_all_of_diff
= start
<= span
[0] and end
>= span
[1]
162 has_head_of_diff
= end
>= span
[0] and end
<= span
[1]
164 selected_diff
=(has_end_of_diff
169 diff
= os
.linesep
.join(self
.__diffs
[idx
])
175 def parse_diff(self
, diff
):
177 for idx
, line
in enumerate(diff
.splitlines()):
179 if self
.__diff
_header
.match(line
):
180 self
.__diffs
.append( [line
] )
182 line_len
= len(line
) + 1
183 self
.__diff
_spans
.append([total_offset
,
184 total_offset
+ line_len
])
186 total_offset
+= line_len
187 self
.__diff
_offsets
.append(total_offset
)
192 errmsg
= 'Malformed diff?\n\n%s' % diff
193 raise AssertionError, errmsg
195 line_len
= len(line
) + 1
196 total_offset
+= line_len
198 self
.__diffs
[self
.__idx
].append(line
)
199 self
.__diff
_spans
[-1][-1] += line_len
200 self
.__diff
_offsets
[self
.__idx
] += line_len