148 lines · 5.6 KB · Python
RawDownload
# Extension to format a paragraph # Does basic, standard text formatting, and also understands Python# comment blocks.  Thus, for editing Python source code, this# extension is really only suitable for reformatting these comment# blocks or triple-quoted strings. # Known problems with comment reformatting:# * If there is a selection marked, and the first line of the#   selection is not complete, the block will probably not be detected#   as comments, and will have the normal "text formatting" rules#   applied.# * If a comment block has leading whitespace that mixes tabs and#   spaces, they will not be considered part of the same block.# * Fancy comments, like this bulleted list, arent handled :-) import refrom configHandler import idleConf class FormatParagraph:     menudefs = [        ('format', [   # /s/edit/format   dscherer@cmu.edu            ('Format Paragraph', '<<format-paragraph>>'),         ])    ]     def __init__(self, editwin):        self.editwin = editwin     def close(self):        self.editwin = None     def format_paragraph_event(self, event):        maxformatwidth = int(idleConf.GetOption('main','FormatParagraph','paragraph'))        text = self.editwin.text        first, last = self.editwin.get_selection_indices()        if first and last:            data = text.get(first, last)            comment_header = ''        else:            first, last, comment_header, data = \                    find_paragraph(text, text.index("insert"))        if comment_header:            # Reformat the comment lines - convert to text sans header.            lines = data.split("\n")            lines = map(lambda st, l=len(comment_header): st[l:], lines)            data = "\n".join(lines)            # Reformat to maxformatwidth chars or a 20 char width, whichever is greater.            format_width = max(maxformatwidth - len(comment_header), 20)            newdata = reformat_paragraph(data, format_width)            # re-split and re-insert the comment header.            newdata = newdata.split("\n")            # If the block ends in a \n, we dont want the comment            # prefix inserted after it. (Im not sure it makes sense to            # reformat a comment block that isnt made of complete            # lines, but whatever!)  Can't think of a clean soltution,            # so we hack away            block_suffix = ""            if not newdata[-1]:                block_suffix = "\n"                newdata = newdata[:-1]            builder = lambda item, prefix=comment_header: prefix+item            newdata = '\n'.join(map(builder, newdata)) + block_suffix        else:            # Just a normal text format            newdata = reformat_paragraph(data, maxformatwidth)        text.tag_remove("sel", "1.0", "end")        if newdata != data:            text.mark_set("insert", first)            text.undo_block_start()            text.delete(first, last)            text.insert(first, newdata)            text.undo_block_stop()        else:            text.mark_set("insert", last)        text.see("insert") def find_paragraph(text, mark):    lineno, col = map(int, mark.split("."))    line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)    while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line):        lineno = lineno + 1        line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)    first_lineno = lineno    comment_header = get_comment_header(line)    comment_header_len = len(comment_header)    while get_comment_header(line)==comment_header and \              not is_all_white(line[comment_header_len:]):        lineno = lineno + 1        line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)    last = "%d.0" % lineno    # Search back to beginning of paragraph    lineno = first_lineno - 1    line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)    while lineno > 0 and \              get_comment_header(line)==comment_header and \              not is_all_white(line[comment_header_len:]):        lineno = lineno - 1        line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)    first = "%d.0" % (lineno+1)    return first, last, comment_header, text.get(first, last) def reformat_paragraph(data, limit):    lines = data.split("\n")    i = 0    n = len(lines)    while i < n and is_all_white(lines[i]):        i = i+1    if i >= n:        return data    indent1 = get_indent(lines[i])    if i+1 < n and not is_all_white(lines[i+1]):        indent2 = get_indent(lines[i+1])    else:        indent2 = indent1    new = lines[:i]    partial = indent1    while i < n and not is_all_white(lines[i]):        # XXX Should take double space after period (etc.) into account        words = re.split("(\s+)", lines[i])        for j in range(0, len(words), 2):            word = words[j]            if not word:                continue # Can happen when line ends in whitespace            if len((partial + word).expandtabs()) > limit and \               partial != indent1:                new.append(partial.rstrip())                partial = indent2            partial = partial + word + " "            if j+1 < len(words) and words[j+1] != " ":                partial = partial + " "        i = i+1    new.append(partial.rstrip())    # XXX Should reformat remaining paragraphs as well    new.extend(lines[i:])    return "\n".join(new) def is_all_white(line):    return re.match(r"^\s*$", line) is not None def get_indent(line):    return re.match(r"^(\s*)", line).group() def get_comment_header(line):    m = re.match(r"^(\s*#*)", line)    if m is None: return ""    return m.group(1)