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