home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 June / PCWorld_2005-06_cd.bin / software / vyzkuste / firewally / firewally.exe / framework-2.3.exe / FormatParagraph.py < prev    next >
Text File  |  2003-12-30  |  6KB  |  147 lines

  1. # Extension to format a paragraph
  2.  
  3. # Does basic, standard text formatting, and also understands Python
  4. # comment blocks.  Thus, for editing Python source code, this
  5. # extension is really only suitable for reformatting these comment
  6. # blocks or triple-quoted strings.
  7.  
  8. # Known problems with comment reformatting:
  9. # * If there is a selection marked, and the first line of the
  10. #   selection is not complete, the block will probably not be detected
  11. #   as comments, and will have the normal "text formatting" rules
  12. #   applied.
  13. # * If a comment block has leading whitespace that mixes tabs and
  14. #   spaces, they will not be considered part of the same block.
  15. # * Fancy comments, like this bulleted list, arent handled :-)
  16.  
  17. import re
  18.  
  19. class FormatParagraph:
  20.  
  21.     menudefs = [
  22.         ('format', [   # /s/edit/format   dscherer@cmu.edu
  23.             ('Format Paragraph', '<<format-paragraph>>'),
  24.          ])
  25.     ]
  26.  
  27.     def __init__(self, editwin):
  28.         self.editwin = editwin
  29.  
  30.     def close(self):
  31.         self.editwin = None
  32.  
  33.     def format_paragraph_event(self, event):
  34.         text = self.editwin.text
  35.         first, last = self.editwin.get_selection_indices()
  36.         if first and last:
  37.             data = text.get(first, last)
  38.             comment_header = ''
  39.         else:
  40.             first, last, comment_header, data = \
  41.                     find_paragraph(text, text.index("insert"))
  42.         if comment_header:
  43.             # Reformat the comment lines - convert to text sans header.
  44.             lines = data.split("\n")
  45.             lines = map(lambda st, l=len(comment_header): st[l:], lines)
  46.             data = "\n".join(lines)
  47.             # Reformat to 70 chars or a 20 char width, whichever is greater.
  48.             format_width = max(70-len(comment_header), 20)
  49.             newdata = reformat_paragraph(data, format_width)
  50.             # re-split and re-insert the comment header.
  51.             newdata = newdata.split("\n")
  52.             # If the block ends in a \n, we dont want the comment
  53.             # prefix inserted after it. (Im not sure it makes sense to
  54.             # reformat a comment block that isnt made of complete
  55.             # lines, but whatever!)  Can't think of a clean soltution,
  56.             # so we hack away
  57.             block_suffix = ""
  58.             if not newdata[-1]:
  59.                 block_suffix = "\n"
  60.                 newdata = newdata[:-1]
  61.             builder = lambda item, prefix=comment_header: prefix+item
  62.             newdata = '\n'.join(map(builder, newdata)) + block_suffix
  63.         else:
  64.             # Just a normal text format
  65.             newdata = reformat_paragraph(data)
  66.         text.tag_remove("sel", "1.0", "end")
  67.         if newdata != data:
  68.             text.mark_set("insert", first)
  69.             text.undo_block_start()
  70.             text.delete(first, last)
  71.             text.insert(first, newdata)
  72.             text.undo_block_stop()
  73.         else:
  74.             text.mark_set("insert", last)
  75.         text.see("insert")
  76.  
  77. def find_paragraph(text, mark):
  78.     lineno, col = map(int, mark.split("."))
  79.     line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
  80.     while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line):
  81.         lineno = lineno + 1
  82.         line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
  83.     first_lineno = lineno
  84.     comment_header = get_comment_header(line)
  85.     comment_header_len = len(comment_header)
  86.     while get_comment_header(line)==comment_header and \
  87.               not is_all_white(line[comment_header_len:]):
  88.         lineno = lineno + 1
  89.         line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
  90.     last = "%d.0" % lineno
  91.     # Search back to beginning of paragraph
  92.     lineno = first_lineno - 1
  93.     line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
  94.     while lineno > 0 and \
  95.               get_comment_header(line)==comment_header and \
  96.               not is_all_white(line[comment_header_len:]):
  97.         lineno = lineno - 1
  98.         line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
  99.     first = "%d.0" % (lineno+1)
  100.     return first, last, comment_header, text.get(first, last)
  101.  
  102. def reformat_paragraph(data, limit=70):
  103.     lines = data.split("\n")
  104.     i = 0
  105.     n = len(lines)
  106.     while i < n and is_all_white(lines[i]):
  107.         i = i+1
  108.     if i >= n:
  109.         return data
  110.     indent1 = get_indent(lines[i])
  111.     if i+1 < n and not is_all_white(lines[i+1]):
  112.         indent2 = get_indent(lines[i+1])
  113.     else:
  114.         indent2 = indent1
  115.     new = lines[:i]
  116.     partial = indent1
  117.     while i < n and not is_all_white(lines[i]):
  118.         # XXX Should take double space after period (etc.) into account
  119.         words = re.split("(\s+)", lines[i])
  120.         for j in range(0, len(words), 2):
  121.             word = words[j]
  122.             if not word:
  123.                 continue # Can happen when line ends in whitespace
  124.             if len((partial + word).expandtabs()) > limit and \
  125.                partial != indent1:
  126.                 new.append(partial.rstrip())
  127.                 partial = indent2
  128.             partial = partial + word + " "
  129.             if j+1 < len(words) and words[j+1] != " ":
  130.                 partial = partial + " "
  131.         i = i+1
  132.     new.append(partial.rstrip())
  133.     # XXX Should reformat remaining paragraphs as well
  134.     new.extend(lines[i:])
  135.     return "\n".join(new)
  136.  
  137. def is_all_white(line):
  138.     return re.match(r"^\s*$", line) is not None
  139.  
  140. def get_indent(line):
  141.     return re.match(r"^(\s*)", line).group()
  142.  
  143. def get_comment_header(line):
  144.     m = re.match(r"^(\s*#*)", line)
  145.     if m is None: return ""
  146.     return m.group(1)
  147.