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)
90 input.replace (quotes
,
91 ("'" * (len(quotes
)/4)) + "\"'")
93 input = "'%s'" % input
94 if input.startswith ("''"):
97 if input.endswith ("''"):
100 return ' '.join (ret
)
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
))
118 class DiffParser (object):
119 def __init__ (self
, diff
):
120 self
.__diff
_header
= re
.compile ('^@@\s[^@]+\s@@.*')
124 self
.__diff
_spans
= []
125 self
.__diff
_offsets
= []
127 self
.parse_diff (diff
)
129 def get_diffs (self
):
132 def get_spans (self
):
133 return self
.__diff
_spans
135 def get_offsets (self
):
136 return self
.__diff
_offsets
138 def get_diff_for_offset (self
, offset
):
139 for idx
, diff_offset
in enumerate (self
.__diff
_offsets
):
140 if offset
< diff_offset
:
141 return os
.linesep
.join (self
.__diffs
[idx
])
144 def get_diffs_for_range (self
, start
, end
):
146 for idx
, span
in enumerate (self
.__diff
_spans
):
148 has_end_of_diff
= start
>= span
[0] and start
< span
[1]
149 has_all_of_diff
= start
<= span
[0] and end
>= span
[1]
150 has_head_of_diff
= end
>= span
[0] and end
<= span
[1]
152 selected_diff
= (has_end_of_diff
157 diff
= os
.linesep
.join (self
.__diffs
[idx
])
163 def parse_diff (self
, diff
):
165 for idx
, line
in enumerate (diff
.splitlines()):
167 if self
.__diff
_header
.match (line
):
168 self
.__diffs
.append ( [line
] )
170 line_len
= len (line
) + 1
171 self
.__diff
_spans
.append ([total_offset
,
172 total_offset
+ line_len
])
174 total_offset
+= line_len
175 self
.__diff
_offsets
.append (total_offset
)
180 errmsg
= 'Malformed diff?\n\n%s' % diff
181 raise AssertionError, errmsg
183 line_len
= len (line
) + 1
184 total_offset
+= line_len
186 self
.__diffs
[self
.__idx
].append (line
)
187 self
.__diff
_spans
[-1][-1] += line_len
188 self
.__diff
_offsets
[self
.__idx
] += line_len