summaryrefslogtreecommitdiffstats
path: root/Tools/idle/FormatParagraph.py
blob: f8827e7615872423e3e139c2ba308cff680bf62c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# Extension to format a paragraph

import string
import re

class FormatParagraph:

    menudefs = [
        ('edit', [
            ('Format Paragraph', '<<format-paragraph>>'),
         ])
    ]

    keydefs = {
        '<<format-paragraph>>': ['<Alt-q>'],
    }
    
    unix_keydefs = {
        '<<format-paragraph>>': ['<Meta-q>'],
    } 

    def __init__(self, editwin):
        self.editwin = editwin

    def format_paragraph_event(self, event):
        text = self.editwin.text
        try:
            first = text.index("sel.first")
            last = text.index("sel.last")
        except TclError:
            first = last = None
        if first and last:
            data = text.get(first, last)
        else:
            first, last, data = find_paragraph(text, text.index("insert"))
        newdata = reformat_paragraph(data)
        text.tag_remove("sel", "1.0", "end")
        if newdata != data:
            text.mark_set("insert", first)
            text.delete(first, last)
            text.insert(first, newdata)
        else:
            text.mark_set("insert", last)
        text.see("insert")

def find_paragraph(text, mark):
    lineno, col = map(int, string.split(mark, "."))
    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
    while not is_all_white(line):
        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 not is_all_white(line):
        lineno = lineno - 1
        line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
    first = "%d.0" % (lineno+1)
    return first, last, text.get(first, last)

def reformat_paragraph(data, limit=70):
    lines = string.split(data, "\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(string.expandtabs(partial + word)) > limit and \
               partial != indent1:
                new.append(string.rstrip(partial))
                partial = indent2
            partial = partial + word + " "
            if j+1 < len(words) and words[j+1] != " ":
                partial = partial + " "
        i = i+1
    new.append(string.rstrip(partial))
    # XXX Should reformat remaining paragraphs as well
    new.extend(lines[i:])
    return string.join(new, "\n")

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()