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

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