"Fossies" - the Fresh Open Source Software Archive

Member "viewvc-1.2.1/lib/idiff.py" (26 Mar 2020, 5734 Bytes) of package /linux/misc/viewvc-1.2.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "idiff.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.1.28_vs_1.2.1.

    1 # -*-python-*-
    2 #
    3 # Copyright (C) 1999-2020 The ViewCVS Group. All Rights Reserved.
    4 #
    5 # By using this file, you agree to the terms and conditions set forth in
    6 # the LICENSE.html file which can be found at the top level of the ViewVC
    7 # distribution or at http://viewvc.org/license-1.html.
    8 #
    9 # For more information, visit http://viewvc.org/
   10 #
   11 # -----------------------------------------------------------------------
   12 #
   13 # idiff: display differences between files highlighting intraline changes
   14 #
   15 # -----------------------------------------------------------------------
   16 
   17 from __future__ import generators
   18 
   19 import difflib
   20 import sys
   21 import re
   22 
   23 from common import _item, _RCSDIFF_NO_CHANGES
   24 import ezt
   25 import sapi
   26 
   27 def sidebyside(fromlines, tolines, context):
   28   """Generate side by side diff"""
   29 
   30   ### for some reason mdiff chokes on \n's in input lines
   31   line_strip = lambda line: line.rstrip("\n")
   32   fromlines = map(line_strip, fromlines)
   33   tolines = map(line_strip, tolines)
   34   had_changes = 0
   35 
   36   gap = False
   37   for fromdata, todata, flag in difflib._mdiff(fromlines, tolines, context):
   38     if fromdata is None and todata is None and flag is None:
   39       gap = True
   40     else:
   41       from_item = _mdiff_split(flag, fromdata)
   42       to_item = _mdiff_split(flag, todata)
   43       had_changes = 1
   44       yield _item(gap=ezt.boolean(gap), columns=(from_item, to_item), type="intraline")
   45       gap = False
   46   if not had_changes:
   47     yield _item(type=_RCSDIFF_NO_CHANGES)
   48 
   49 _re_mdiff = re.compile("\0([+-^])(.*?)\1")
   50 
   51 def _mdiff_split(flag, (line_number, text)):
   52   """Break up row from mdiff output into segments"""
   53   segments = []
   54   pos = 0
   55   while True:
   56     m = _re_mdiff.search(text, pos)
   57     if not m:
   58       segments.append(_item(text=sapi.escape(text[pos:]), type=None))
   59       break
   60 
   61     if m.start() > pos:
   62       segments.append(_item(text=sapi.escape(text[pos:m.start()]), type=None))
   63 
   64     if m.group(1) == "+":
   65       segments.append(_item(text=sapi.escape(m.group(2)), type="add"))
   66     elif m.group(1) == "-":
   67       segments.append(_item(text=sapi.escape(m.group(2)), type="remove"))
   68     elif m.group(1) == "^":
   69       segments.append(_item(text=sapi.escape(m.group(2)), type="change"))
   70 
   71     pos = m.end()
   72 
   73   return _item(segments=segments, line_number=line_number)  
   74 
   75 def unified(fromlines, tolines, context):
   76   """Generate unified diff"""
   77 
   78   diff = difflib.Differ().compare(fromlines, tolines)
   79   lastrow = None
   80   had_changes = 0
   81 
   82   for row in _trim_context(diff, context):
   83     if row[0].startswith("? "):
   84       had_changes = 1
   85       yield _differ_split(lastrow, row[0])
   86       lastrow = None
   87     else:
   88       if lastrow:
   89         had_changes = 1
   90         yield _differ_split(lastrow, None)
   91       lastrow = row
   92 
   93   if lastrow:
   94     had_changes = 1
   95     yield _differ_split(lastrow, None)
   96 
   97   if not had_changes:
   98     yield _item(type=_RCSDIFF_NO_CHANGES)
   99 
  100 def _trim_context(lines, context_size):
  101   """Trim context lines that don't surround changes from Differ results
  102 
  103   yields (line, leftnum, rightnum, gap) tuples"""
  104 
  105   # circular buffer to hold context lines
  106   context_buffer = [None] * (context_size or 0)
  107   context_start = context_len = 0
  108 
  109   # number of context lines left to print after encountering a change
  110   context_owed = 0
  111 
  112   # current line numbers
  113   leftnum = rightnum = 0
  114 
  115   # whether context lines have been dropped
  116   gap = False
  117 
  118   for line in lines:
  119     row = save = None
  120 
  121     if line.startswith("- "):
  122       leftnum = leftnum + 1
  123       row = line, leftnum, None
  124       context_owed = context_size
  125 
  126     elif line.startswith("+ "):
  127       rightnum = rightnum + 1
  128       row = line, None, rightnum
  129       context_owed = context_size
  130 
  131     else:
  132       if line.startswith("  "):
  133         leftnum = leftnum = leftnum + 1
  134         rightnum = rightnum = rightnum + 1
  135         if context_owed > 0:
  136           context_owed = context_owed - 1
  137         elif context_size is not None:
  138           save = True
  139 
  140       row = line, leftnum, rightnum
  141 
  142     if save:
  143       # don't yield row right away, store it in buffer
  144       context_buffer[(context_start + context_len) % context_size] = row
  145       if context_len == context_size:
  146         context_start = (context_start + 1) % context_size
  147         gap = True
  148       else:
  149         context_len = context_len + 1
  150     else:
  151       # yield row, but first drain stuff in buffer
  152       context_len == context_size
  153       while context_len:
  154         yield context_buffer[context_start] + (gap,)
  155         gap = False
  156         context_start = (context_start + 1) % context_size
  157         context_len = context_len - 1
  158       yield row + (gap,)
  159       gap = False
  160 
  161 _re_differ = re.compile(r"[+-^]+")
  162 
  163 def _differ_split(row, guide):
  164   """Break row into segments using guide line"""
  165   line, left_number, right_number, gap = row
  166 
  167   if left_number and right_number:
  168     type = "" 
  169   elif left_number:
  170     type = "remove"
  171   elif right_number:
  172     type = "add"
  173 
  174   segments = []  
  175   pos = 2
  176 
  177   if guide:
  178     assert guide.startswith("? ")
  179 
  180     for m in _re_differ.finditer(guide, pos):
  181       if m.start() > pos:
  182         segments.append(_item(text=sapi.escape(line[pos:m.start()]), type=None))
  183       segments.append(_item(text=sapi.escape(line[m.start():m.end()]),
  184                             type="change"))
  185       pos = m.end()
  186 
  187   segments.append(_item(text=sapi.escape(line[pos:]), type=None))
  188 
  189   return _item(gap=ezt.boolean(gap), type=type, segments=segments,
  190                left_number=left_number, right_number=right_number)
  191 
  192 try:
  193   ### Using difflib._mdiff function here was the easiest way of obtaining
  194   ### intraline diffs for use in ViewVC, but it doesn't exist prior to
  195   ### Python 2.4 and is not part of the public difflib API, so for now
  196   ### fall back if it doesn't exist.
  197   difflib._mdiff
  198 except AttributeError:
  199   sidebyside = None