PyXR

c:\python24\lib \ idlelib \ FormatParagraph.py



0001 # Extension to format a paragraph
0002 
0003 # Does basic, standard text formatting, and also understands Python
0004 # comment blocks.  Thus, for editing Python source code, this
0005 # extension is really only suitable for reformatting these comment
0006 # blocks or triple-quoted strings.
0007 
0008 # Known problems with comment reformatting:
0009 # * If there is a selection marked, and the first line of the
0010 #   selection is not complete, the block will probably not be detected
0011 #   as comments, and will have the normal "text formatting" rules
0012 #   applied.
0013 # * If a comment block has leading whitespace that mixes tabs and
0014 #   spaces, they will not be considered part of the same block.
0015 # * Fancy comments, like this bulleted list, arent handled :-)
0016 
0017 import re
0018 from configHandler import idleConf
0019 
0020 class FormatParagraph:
0021 
0022     menudefs = [
0023         ('format', [   # /s/edit/format   dscherer@cmu.edu
0024             ('Format Paragraph', '<<format-paragraph>>'),
0025          ])
0026     ]
0027 
0028     def __init__(self, editwin):
0029         self.editwin = editwin
0030 
0031     def close(self):
0032         self.editwin = None
0033 
0034     def format_paragraph_event(self, event):
0035         maxformatwidth = int(idleConf.GetOption('main','FormatParagraph','paragraph'))
0036         text = self.editwin.text
0037         first, last = self.editwin.get_selection_indices()
0038         if first and last:
0039             data = text.get(first, last)
0040             comment_header = ''
0041         else:
0042             first, last, comment_header, data = \
0043                     find_paragraph(text, text.index("insert"))
0044         if comment_header:
0045             # Reformat the comment lines - convert to text sans header.
0046             lines = data.split("\n")
0047             lines = map(lambda st, l=len(comment_header): st[l:], lines)
0048             data = "\n".join(lines)
0049             # Reformat to maxformatwidth chars or a 20 char width, whichever is greater.
0050             format_width = max(maxformatwidth - len(comment_header), 20)
0051             newdata = reformat_paragraph(data, format_width)
0052             # re-split and re-insert the comment header.
0053             newdata = newdata.split("\n")
0054             # If the block ends in a \n, we dont want the comment
0055             # prefix inserted after it. (Im not sure it makes sense to
0056             # reformat a comment block that isnt made of complete
0057             # lines, but whatever!)  Can't think of a clean soltution,
0058             # so we hack away
0059             block_suffix = ""
0060             if not newdata[-1]:
0061                 block_suffix = "\n"
0062                 newdata = newdata[:-1]
0063             builder = lambda item, prefix=comment_header: prefix+item
0064             newdata = '\n'.join(map(builder, newdata)) + block_suffix
0065         else:
0066             # Just a normal text format
0067             newdata = reformat_paragraph(data, maxformatwidth)
0068         text.tag_remove("sel", "1.0", "end")
0069         if newdata != data:
0070             text.mark_set("insert", first)
0071             text.undo_block_start()
0072             text.delete(first, last)
0073             text.insert(first, newdata)
0074             text.undo_block_stop()
0075         else:
0076             text.mark_set("insert", last)
0077         text.see("insert")
0078 
0079 def find_paragraph(text, mark):
0080     lineno, col = map(int, mark.split("."))
0081     line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
0082     while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line):
0083         lineno = lineno + 1
0084         line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
0085     first_lineno = lineno
0086     comment_header = get_comment_header(line)
0087     comment_header_len = len(comment_header)
0088     while get_comment_header(line)==comment_header and \
0089               not is_all_white(line[comment_header_len:]):
0090         lineno = lineno + 1
0091         line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
0092     last = "%d.0" % lineno
0093     # Search back to beginning of paragraph
0094     lineno = first_lineno - 1
0095     line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
0096     while lineno > 0 and \
0097               get_comment_header(line)==comment_header and \
0098               not is_all_white(line[comment_header_len:]):
0099         lineno = lineno - 1
0100         line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
0101     first = "%d.0" % (lineno+1)
0102     return first, last, comment_header, text.get(first, last)
0103 
0104 def reformat_paragraph(data, limit):
0105     lines = data.split("\n")
0106     i = 0
0107     n = len(lines)
0108     while i < n and is_all_white(lines[i]):
0109         i = i+1
0110     if i >= n:
0111         return data
0112     indent1 = get_indent(lines[i])
0113     if i+1 < n and not is_all_white(lines[i+1]):
0114         indent2 = get_indent(lines[i+1])
0115     else:
0116         indent2 = indent1
0117     new = lines[:i]
0118     partial = indent1
0119     while i < n and not is_all_white(lines[i]):
0120         # XXX Should take double space after period (etc.) into account
0121         words = re.split("(\s+)", lines[i])
0122         for j in range(0, len(words), 2):
0123             word = words[j]
0124             if not word:
0125                 continue # Can happen when line ends in whitespace
0126             if len((partial + word).expandtabs()) > limit and \
0127                partial != indent1:
0128                 new.append(partial.rstrip())
0129                 partial = indent2
0130             partial = partial + word + " "
0131             if j+1 < len(words) and words[j+1] != " ":
0132                 partial = partial + " "
0133         i = i+1
0134     new.append(partial.rstrip())
0135     # XXX Should reformat remaining paragraphs as well
0136     new.extend(lines[i:])
0137     return "\n".join(new)
0138 
0139 def is_all_white(line):
0140     return re.match(r"^\s*$", line) is not None
0141 
0142 def get_indent(line):
0143     return re.match(r"^(\s*)", line).group()
0144 
0145 def get_comment_header(line):
0146     m = re.match(r"^(\s*#*)", line)
0147     if m is None: return ""
0148     return m.group(1)
0149 

Generated by PyXR 0.9.4
SourceForge.net Logo