ยปCore Development>Code coverage>Lib/idlelib/FormatParagraph.py

Python code coverage for Lib/idlelib/FormatParagraph.py

#countcontent
1n/a"""Extension to format a paragraph or selection to a max width.
2n/a
3n/aDoes basic, standard text formatting, and also understands Python
4n/acomment blocks. Thus, for editing Python source code, this
5n/aextension is really only suitable for reformatting these comment
6n/ablocks or triple-quoted strings.
7n/a
8n/aKnown problems with comment reformatting:
9n/a* If there is a selection marked, and the first line of the
10n/a selection is not complete, the block will probably not be detected
11n/a as comments, and will have the normal "text formatting" rules
12n/a applied.
13n/a* If a comment block has leading whitespace that mixes tabs and
14n/a spaces, they will not be considered part of the same block.
15n/a* Fancy comments, like this bulleted list, aren't handled :-)
16n/a"""
17n/a
18n/aimport re
19n/afrom idlelib.configHandler import idleConf
20n/a
21n/aclass FormatParagraph:
22n/a
23n/a menudefs = [
24n/a ('format', [ # /s/edit/format dscherer@cmu.edu
25n/a ('Format Paragraph', '<<format-paragraph>>'),
26n/a ])
27n/a ]
28n/a
29n/a def __init__(self, editwin):
30n/a self.editwin = editwin
31n/a
32n/a def close(self):
33n/a self.editwin = None
34n/a
35n/a def format_paragraph_event(self, event):
36n/a """Formats paragraph to a max width specified in idleConf.
37n/a
38n/a If text is selected, format_paragraph_event will start breaking lines
39n/a at the max width, starting from the beginning selection.
40n/a
41n/a If no text is selected, format_paragraph_event uses the current
42n/a cursor location to determine the paragraph (lines of text surrounded
43n/a by blank lines) and formats it.
44n/a """
45n/a maxformatwidth = idleConf.GetOption(
46n/a 'main', 'FormatParagraph', 'paragraph', type='int')
47n/a text = self.editwin.text
48n/a first, last = self.editwin.get_selection_indices()
49n/a if first and last:
50n/a data = text.get(first, last)
51n/a comment_header = get_comment_header(data)
52n/a else:
53n/a first, last, comment_header, data = \
54n/a find_paragraph(text, text.index("insert"))
55n/a if comment_header:
56n/a newdata = reformat_comment(data, maxformatwidth, comment_header)
57n/a else:
58n/a newdata = reformat_paragraph(data, maxformatwidth)
59n/a text.tag_remove("sel", "1.0", "end")
60n/a
61n/a if newdata != data:
62n/a text.mark_set("insert", first)
63n/a text.undo_block_start()
64n/a text.delete(first, last)
65n/a text.insert(first, newdata)
66n/a text.undo_block_stop()
67n/a else:
68n/a text.mark_set("insert", last)
69n/a text.see("insert")
70n/a return "break"
71n/a
72n/adef find_paragraph(text, mark):
73n/a """Returns the start/stop indices enclosing the paragraph that mark is in.
74n/a
75n/a Also returns the comment format string, if any, and paragraph of text
76n/a between the start/stop indices.
77n/a """
78n/a lineno, col = map(int, mark.split("."))
79n/a line = text.get("%d.0" % lineno, "%d.end" % lineno)
80n/a
81n/a # Look for start of next paragraph if the index passed in is a blank line
82n/a while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line):
83n/a lineno = lineno + 1
84n/a line = text.get("%d.0" % lineno, "%d.end" % lineno)
85n/a first_lineno = lineno
86n/a comment_header = get_comment_header(line)
87n/a comment_header_len = len(comment_header)
88n/a
89n/a # Once start line found, search for end of paragraph (a blank line)
90n/a while get_comment_header(line)==comment_header and \
91n/a not is_all_white(line[comment_header_len:]):
92n/a lineno = lineno + 1
93n/a line = text.get("%d.0" % lineno, "%d.end" % lineno)
94n/a last = "%d.0" % lineno
95n/a
96n/a # Search back to beginning of paragraph (first blank line before)
97n/a lineno = first_lineno - 1
98n/a line = text.get("%d.0" % lineno, "%d.end" % lineno)
99n/a while lineno > 0 and \
100n/a get_comment_header(line)==comment_header and \
101n/a not is_all_white(line[comment_header_len:]):
102n/a lineno = lineno - 1
103n/a line = text.get("%d.0" % lineno, "%d.end" % lineno)
104n/a first = "%d.0" % (lineno+1)
105n/a
106n/a return first, last, comment_header, text.get(first, last)
107n/a
108n/a# This should perhaps be replaced with textwrap.wrap
109n/adef reformat_paragraph(data, limit):
110n/a """Return data reformatted to specified width (limit)."""
111n/a lines = data.split("\n")
112n/a i = 0
113n/a n = len(lines)
114n/a while i < n and is_all_white(lines[i]):
115n/a i = i+1
116n/a if i >= n:
117n/a return data
118n/a indent1 = get_indent(lines[i])
119n/a if i+1 < n and not is_all_white(lines[i+1]):
120n/a indent2 = get_indent(lines[i+1])
121n/a else:
122n/a indent2 = indent1
123n/a new = lines[:i]
124n/a partial = indent1
125n/a while i < n and not is_all_white(lines[i]):
126n/a # XXX Should take double space after period (etc.) into account
127n/a words = re.split("(\s+)", lines[i])
128n/a for j in range(0, len(words), 2):
129n/a word = words[j]
130n/a if not word:
131n/a continue # Can happen when line ends in whitespace
132n/a if len((partial + word).expandtabs()) > limit and \
133n/a partial != indent1:
134n/a new.append(partial.rstrip())
135n/a partial = indent2
136n/a partial = partial + word + " "
137n/a if j+1 < len(words) and words[j+1] != " ":
138n/a partial = partial + " "
139n/a i = i+1
140n/a new.append(partial.rstrip())
141n/a # XXX Should reformat remaining paragraphs as well
142n/a new.extend(lines[i:])
143n/a return "\n".join(new)
144n/a
145n/adef reformat_comment(data, limit, comment_header):
146n/a """Return data reformatted to specified width with comment header."""
147n/a
148n/a # Remove header from the comment lines
149n/a lc = len(comment_header)
150n/a data = "\n".join(line[lc:] for line in data.split("\n"))
151n/a # Reformat to maxformatwidth chars or a 20 char width,
152n/a # whichever is greater.
153n/a format_width = max(limit - len(comment_header), 20)
154n/a newdata = reformat_paragraph(data, format_width)
155n/a # re-split and re-insert the comment header.
156n/a newdata = newdata.split("\n")
157n/a # If the block ends in a \n, we dont want the comment prefix
158n/a # inserted after it. (Im not sure it makes sense to reformat a
159n/a # comment block that is not made of complete lines, but whatever!)
160n/a # Can't think of a clean solution, so we hack away
161n/a block_suffix = ""
162n/a if not newdata[-1]:
163n/a block_suffix = "\n"
164n/a newdata = newdata[:-1]
165n/a return '\n'.join(comment_header+line for line in newdata) + block_suffix
166n/a
167n/adef is_all_white(line):
168n/a """Return True if line is empty or all whitespace."""
169n/a
170n/a return re.match(r"^\s*$", line) is not None
171n/a
172n/adef get_indent(line):
173n/a """Return the initial space or tab indent of line."""
174n/a return re.match(r"^([ \t]*)", line).group()
175n/a
176n/adef get_comment_header(line):
177n/a """Return string with leading whitespace and '#' from line or ''.
178n/a
179n/a A null return indicates that the line is not a comment line. A non-
180n/a null return, such as ' #', will be used to find the other lines of
181n/a a comment block with the same indent.
182n/a """
183n/a m = re.match(r"^([ \t]*#*)", line)
184n/a if m is None: return ""
185n/a return m.group(1)
186n/a
187n/aif __name__ == "__main__":
188n/a from test import support; support.use_resources = ['gui']
189n/a import unittest
190n/a unittest.main('idlelib.idle_test.test_formatparagraph',
191n/a verbosity=2, exit=False)