python-2.5.2/win32/Lib/idlelib/FormatParagraph.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     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 from configHandler import idleConf
       
    19 
       
    20 class FormatParagraph:
       
    21 
       
    22     menudefs = [
       
    23         ('format', [   # /s/edit/format   dscherer@cmu.edu
       
    24             ('Format Paragraph', '<<format-paragraph>>'),
       
    25          ])
       
    26     ]
       
    27 
       
    28     def __init__(self, editwin):
       
    29         self.editwin = editwin
       
    30 
       
    31     def close(self):
       
    32         self.editwin = None
       
    33 
       
    34     def format_paragraph_event(self, event):
       
    35         maxformatwidth = int(idleConf.GetOption('main','FormatParagraph','paragraph'))
       
    36         text = self.editwin.text
       
    37         first, last = self.editwin.get_selection_indices()
       
    38         if first and last:
       
    39             data = text.get(first, last)
       
    40             comment_header = ''
       
    41         else:
       
    42             first, last, comment_header, data = \
       
    43                     find_paragraph(text, text.index("insert"))
       
    44         if comment_header:
       
    45             # Reformat the comment lines - convert to text sans header.
       
    46             lines = data.split("\n")
       
    47             lines = map(lambda st, l=len(comment_header): st[l:], lines)
       
    48             data = "\n".join(lines)
       
    49             # Reformat to maxformatwidth chars or a 20 char width, whichever is greater.
       
    50             format_width = max(maxformatwidth - len(comment_header), 20)
       
    51             newdata = reformat_paragraph(data, format_width)
       
    52             # re-split and re-insert the comment header.
       
    53             newdata = newdata.split("\n")
       
    54             # If the block ends in a \n, we dont want the comment
       
    55             # prefix inserted after it. (Im not sure it makes sense to
       
    56             # reformat a comment block that isnt made of complete
       
    57             # lines, but whatever!)  Can't think of a clean soltution,
       
    58             # so we hack away
       
    59             block_suffix = ""
       
    60             if not newdata[-1]:
       
    61                 block_suffix = "\n"
       
    62                 newdata = newdata[:-1]
       
    63             builder = lambda item, prefix=comment_header: prefix+item
       
    64             newdata = '\n'.join(map(builder, newdata)) + block_suffix
       
    65         else:
       
    66             # Just a normal text format
       
    67             newdata = reformat_paragraph(data, maxformatwidth)
       
    68         text.tag_remove("sel", "1.0", "end")
       
    69         if newdata != data:
       
    70             text.mark_set("insert", first)
       
    71             text.undo_block_start()
       
    72             text.delete(first, last)
       
    73             text.insert(first, newdata)
       
    74             text.undo_block_stop()
       
    75         else:
       
    76             text.mark_set("insert", last)
       
    77         text.see("insert")
       
    78         return "break"
       
    79 
       
    80 def find_paragraph(text, mark):
       
    81     lineno, col = map(int, mark.split("."))
       
    82     line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
       
    83     while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line):
       
    84         lineno = lineno + 1
       
    85         line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
       
    86     first_lineno = lineno
       
    87     comment_header = get_comment_header(line)
       
    88     comment_header_len = len(comment_header)
       
    89     while get_comment_header(line)==comment_header and \
       
    90               not is_all_white(line[comment_header_len:]):
       
    91         lineno = lineno + 1
       
    92         line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
       
    93     last = "%d.0" % lineno
       
    94     # Search back to beginning of paragraph
       
    95     lineno = first_lineno - 1
       
    96     line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
       
    97     while lineno > 0 and \
       
    98               get_comment_header(line)==comment_header and \
       
    99               not is_all_white(line[comment_header_len:]):
       
   100         lineno = lineno - 1
       
   101         line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
       
   102     first = "%d.0" % (lineno+1)
       
   103     return first, last, comment_header, text.get(first, last)
       
   104 
       
   105 def reformat_paragraph(data, limit):
       
   106     lines = data.split("\n")
       
   107     i = 0
       
   108     n = len(lines)
       
   109     while i < n and is_all_white(lines[i]):
       
   110         i = i+1
       
   111     if i >= n:
       
   112         return data
       
   113     indent1 = get_indent(lines[i])
       
   114     if i+1 < n and not is_all_white(lines[i+1]):
       
   115         indent2 = get_indent(lines[i+1])
       
   116     else:
       
   117         indent2 = indent1
       
   118     new = lines[:i]
       
   119     partial = indent1
       
   120     while i < n and not is_all_white(lines[i]):
       
   121         # XXX Should take double space after period (etc.) into account
       
   122         words = re.split("(\s+)", lines[i])
       
   123         for j in range(0, len(words), 2):
       
   124             word = words[j]
       
   125             if not word:
       
   126                 continue # Can happen when line ends in whitespace
       
   127             if len((partial + word).expandtabs()) > limit and \
       
   128                partial != indent1:
       
   129                 new.append(partial.rstrip())
       
   130                 partial = indent2
       
   131             partial = partial + word + " "
       
   132             if j+1 < len(words) and words[j+1] != " ":
       
   133                 partial = partial + " "
       
   134         i = i+1
       
   135     new.append(partial.rstrip())
       
   136     # XXX Should reformat remaining paragraphs as well
       
   137     new.extend(lines[i:])
       
   138     return "\n".join(new)
       
   139 
       
   140 def is_all_white(line):
       
   141     return re.match(r"^\s*$", line) is not None
       
   142 
       
   143 def get_indent(line):
       
   144     return re.match(r"^(\s*)", line).group()
       
   145 
       
   146 def get_comment_header(line):
       
   147     m = re.match(r"^(\s*#*)", line)
       
   148     if m is None: return ""
       
   149     return m.group(1)