import os, commands, re, tempfile from subprocess import Popen, PIPE, STDOUT import buffer, default, dirutil, lex, regex, util, window from point import Point from method import Method, Argument class SvnException(Exception): pass statuses = { ' ': 'Unmodified', 'A': 'Added', 'C': 'Conflicted', 'D': 'Deleted', 'I': 'Ignored', 'M': 'Modified', 'R': 'Replaced', 'X': 'External', '?': 'Unknown', '!': 'Missing', '~': 'Obstructed', } def get_status(path, base=None): if base is None: base = os.getcwd() + os.path.sep if path.startswith(base): path = path[len(base):] cmd = "svn status -v %r" % path status, data = commands.getstatusoutput(cmd) status = status >> 8 if status != 0: raise SvnException("Problems with 'svn status': %d" % status) c = data[0] status = statuses.get(c, 'Error (%s)' % c) fields = data[6:].split() try: rrev, lrev, lauthor, filename = fields except: raise Exception, '%r %r' % (fields, data[6:]) return { 'svn-filename': filename, 'svn-status': status, 'svn-lrev': lrev, 'svn-rrev': rrev, 'svn-author': lauthor, } class SvnCommit(Method): '''diff the current file with the version in SVN''' args = [Argument("msg", type=type(""), prompt="Commit Message: ")] regex = re.compile(r'^Committed revision ([0-9]+)\.$') def _execute(self, w, **vargs): if not hasattr(w.buffer, 'path'): w.set_error("Buffer has no corresponding file") return cwd = os.getcwd() + os.path.sep path = w.buffer.path if path.startswith(cwd): path = path[len(cwd):] cmd = "svn ci -m %r %r" % (vargs['msg'], path) (status, data) = commands.getstatusoutput(cmd) status = status >> 8 lines = data.split('\n') if status == 0: try: for line in lines: m = self.regex.match(line) if m: rev = m.group(1) w.buffer.metadata['svn-lrev'] = rev w.buffer.metadata['svn-rrev'] = rev w.set_error("Committed [%s]" % rev) return except: pass w.set_error("Problems with SVN commit: %d" % status) w.application.data_buffer("*Commit*", repr(lines), switch_to=True) class SvnStatus(Method): column = { ' ': 'Unmodified', 'A': 'Added', 'C': 'Conflicted', 'D': 'Deleted', 'I': 'Ignored', 'M': 'Modified', 'R': 'Replaced', 'X': 'External', '?': 'Unknown', '!': 'Missing', '~': 'Obstructed', } def _execute(self, w, **vargs): if not hasattr(w.buffer, 'path'): w.set_error("Buffer has no corresponding file") return cwd = os.getcwd() + os.path.sep path = w.buffer.path if path.startswith(cwd): path = path[len(cwd):] cmd = "svn status -v %r" % path (status, data) = commands.getstatusoutput(cmd) status = status >> 8 if status != 0: w.set_error("Problems with 'svn status': %d" % status) return c = data[0] status = self.column.get(c, 'Error (%s)' % c) fields = data[6:].split() if len(fields) == 4: rrev, lrev, lauthor, filename = fields w.buffer.metadata['svn-filename'] = filename w.buffer.metadata['svn-status'] = status w.buffer.metadata['svn-lrev'] = lrev w.buffer.metadata['svn-rrev'] = rrev w.buffer.metadata['svn-author'] = lauthor w.buffer.metadata['vc-info'] = '[svn:%s/%s]' % (lrev, rrev) w.set_error('%s %s %s/%s [%s]' % (filename, status, rrev, lrev, lauthor)) else: w.buffer.metadata['svn-filename'] = path w.buffer.metadata['svn-status'] = status w.buffer.metadata['vc-info'] = '[svn:%s]' % status.lower() w.set_error('%s %s' % (path, status)) class SvnLog(Method): '''display the SVN log for the current file''' sep_re = re.compile('^-+$') log_re = re.compile(r'^(.+?) \| (.+?) \| (.{25}) .+? \| (.+)$') def _build_entry(self, log_line, mesg_lines): log_data = '[c:d:*]%s [g:d:*]%s [b:d:*]%s [c:d:*]%s' % log_line mesg_data = '\n'.join(mesg_lines).strip() if mesg_data: mesg_data += '\n' return '[b:d:*]' + log_data + '\n' + mesg_data def _execute(self, w, **vargs): cmd = "svn log %r" % w.buffer.path (status, data) = commands.getstatusoutput(cmd) entries = [] log_line, mesg_lines = None, [] for line in data.split('\n'): if self.sep_re.match(line): if log_line is not None: entries.append(self._build_entry(log_line, mesg_lines)) log_line = None else: m = self.log_re.match(line) if m: log_line = m.groups() mesg_lines = [] else: assert log_line is not None, '%r %r' % (entries, line) mesg_lines.append(line) data2 = ''.join(entries) if status == 0 and data: w.application.color_data_buffer("*Log*", data2, switch_to=True) w.set_error("%s: logfile" % self.name) elif status == 0: w.set_error("%s: There was no data" % self.name) else: w.set_error("%s: There was an error (%s)" % (self.name, status)) class SvnDiff(Method): '''diff the current file with the version in SVN''' def _execute(self, w, **vargs): if not hasattr(w.buffer, 'path'): w.set_error("Buffer has no corresponding file") return cmd = "svn diff %r" % w.buffer.path (status, data) = commands.getstatusoutput(cmd) if status == 0: if data: w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff') w.set_error("Differences were found") else: w.set_error("No difference found") else: w.set_error("There was an error (%s)" % (status)) class SvnDiff2(Method): '''diff the current file with the version in SVN''' rev_regex = re.compile('^[0-9]+$') args = [Argument("revision", type=type(""), prompt="Old Revision: ")] def _execute(self, w, **vargs): if not hasattr(w.buffer, 'path'): w.set_error("Buffer has no corresponding file") return rev = vargs['revision'] if not self.rev_regex.match(rev): w.set_error("Could not parse revision: %r" % rev) return cwd = os.getcwd() + os.path.sep path = w.buffer.path if path.startswith(cwd): path = path[len(cwd):] cmd = "svn diff -r %s %r" % (rev, path) (status, data) = commands.getstatusoutput(cmd) status = status >> 8 if data: w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff') w.set_error("Differences were found") else: w.set_error("No difference found") class SvnDiff3(Method): '''diff the current file with the version in SVN''' rev_regex = re.compile('^[0-9]+$') args = [Argument("revision1", type=type(""), prompt="Old Revision: "), Argument("revision2", type=type(""), prompt="New Revision: ")] def _execute(self, w, **vargs): if not hasattr(w.buffer, 'path'): w.set_error("Buffer has no corresponding file") return rev1 = vargs['revision1'] if not self.rev_regex.match(rev1): w.set_error("Could not parse revision1: %r" % rev) return rev2 = vargs['revision2'] if not self.rev_regex.match(rev2): w.set_error("Could not parse revision2: %r" % rev) return cwd = os.getcwd() + os.path.sep path = w.buffer.path if path.startswith(cwd): path = path[len(cwd):] cmd = "svn diff -r %s:%s %r" % (rev1, rev2, path) (status, data) = commands.getstatusoutput(cmd) status = status >> 8 if data: w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff') w.set_error("Differences were found") else: w.set_error("No difference found") class SvnBlame(Method): '''show blame output for the current version in SVN''' line_re = re.compile('^ *(\d+) *([a-zA-Z0-9_]+) *([-0-9]+) *([:0-9]+) *(-\d{4}) *\(([^\)]+)\) (.*)\n$') def _execute(self, w, **vargs): if not hasattr(w.buffer, 'path'): w.set_error("Buffer has no corresponding file") return cmd = ("/usr/bin/svn", 'blame', '-v', w.buffer.path) pipe = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) linetokens = [] for line in pipe.stdout: m = self.line_re.match(line) if not m: raise Exception, line (rev, user, date, t, tz, vdate, content) = m.groups() linetokens.append([rev, user, date, content, []]) status = pipe.wait() >> 8 lines = [x[3] for x in linetokens] if w.mode.grammar: lexer = lex.Lexer(w.mode, w.mode.grammar) lextokens = [[] for l in lines] for t in lexer.lex(lines): linetokens[t.y][4].append(t) lines = [] for linetoken in linetokens: (rev, user, date, content, lextokens) = linetoken prefix = '[b:d:*]%-4s [c:d:*]%-10s [b:d:*]%10s[d:d:*]' % (rev, user, date) if lextokens: suffixes = [] for lt in lextokens: s = lt.string.replace('\\', '\\\\') s = s.replace('[', '\\[').replace(']', '\\]') suffixes.append('[%s:%s:*]%s' % (lt.color[0], lt.color[1], s)) suffix = ''.join(suffixes) else: suffix = content + '\n' lines.append('%s %s' % (prefix, suffix)) data = ''.join(lines) if status == 0: w.application.color_data_buffer("*Blame*", data, switch_to=True) else: w.set_error("There was an error (%s)" % (status))