bpo-23216: IDLE: Add docstrings to search modules (GH-12141) · python/cpython@b34f1aa

11

"""Replace dialog for IDLE. Inherits SearchDialogBase for GUI.

2-

Uses idlelib.SearchEngine for search capability.

2+

Uses idlelib.searchengine.SearchEngine for search capability.

33

Defines various replace related functions like replace, replace all,

4-

replace+find.

4+

and replace+find.

55

"""

66

import re

77

@@ -10,9 +10,16 @@

1010

from idlelib.searchbase import SearchDialogBase

1111

from idlelib import searchengine

121213+1314

def replace(text):

14-

"""Returns a singleton ReplaceDialog instance.The single dialog

15-

saves user entries and preferences across instances."""

15+

"""Create or reuse a singleton ReplaceDialog instance.

16+17+

The singleton dialog saves user entries and preferences

18+

across instances.

19+20+

Args:

21+

text: Text widget containing the text to be searched.

22+

"""

1623

root = text._root()

1724

engine = searchengine.get(root)

1825

if not hasattr(engine, "_replacedialog"):

@@ -22,16 +29,36 @@ def replace(text):

222923302431

class ReplaceDialog(SearchDialogBase):

32+

"Dialog for finding and replacing a pattern in text."

25332634

title = "Replace Dialog"

2735

icon = "Replace"

28362937

def __init__(self, root, engine):

30-

SearchDialogBase.__init__(self, root, engine)

38+

"""Create search dialog for finding and replacing text.

39+40+

Uses SearchDialogBase as the basis for the GUI and a

41+

searchengine instance to prepare the search.

42+43+

Attributes:

44+

replvar: StringVar containing 'Replace with:' value.

45+

replent: Entry widget for replvar. Created in

46+

create_entries().

47+

ok: Boolean used in searchengine.search_text to indicate

48+

whether the search includes the selection.

49+

"""

50+

super().__init__(root, engine)

3151

self.replvar = StringVar(root)

32523353

def open(self, text):

34-

"""Display the replace dialog"""

54+

"""Make dialog visible on top of others and ready to use.

55+56+

Also, highlight the currently selected text and set the

57+

search to include the current selection (self.ok).

58+59+

Args:

60+

text: Text widget being searched.

61+

"""

3562

SearchDialogBase.open(self, text)

3663

try:

3764

first = text.index("sel.first")

@@ -44,37 +71,50 @@ def open(self, text):

4471

first = first or text.index("insert")

4572

last = last or first

4673

self.show_hit(first, last)

47-

self.ok = 1

74+

self.ok = True

48754976

def create_entries(self):

50-

"""Create label and text entry widgets"""

77+

"Create base and additional label and text entry widgets."

5178

SearchDialogBase.create_entries(self)

5279

self.replent = self.make_entry("Replace with:", self.replvar)[0]

53805481

def create_command_buttons(self):

82+

"""Create base and additional command buttons.

83+84+

The additional buttons are for Find, Replace,

85+

Replace+Find, and Replace All.

86+

"""

5587

SearchDialogBase.create_command_buttons(self)

5688

self.make_button("Find", self.find_it)

5789

self.make_button("Replace", self.replace_it)

58-

self.make_button("Replace+Find", self.default_command, 1)

90+

self.make_button("Replace+Find", self.default_command, isdef=True)

5991

self.make_button("Replace All", self.replace_all)

60926193

def find_it(self, event=None):

62-

self.do_find(0)

94+

"Handle the Find button."

95+

self.do_find(False)

63966497

def replace_it(self, event=None):

98+

"""Handle the Replace button.

99+100+

If the find is successful, then perform replace.

101+

"""

65102

if self.do_find(self.ok):

66103

self.do_replace()

6710468105

def default_command(self, event=None):

69-

"Replace and find next."

106+

"""Handle the Replace+Find button as the default command.

107+108+

First performs a replace and then, if the replace was

109+

successful, a find next.

110+

"""

70111

if self.do_find(self.ok):

71112

if self.do_replace(): # Only find next match if replace succeeded.

72113

# A bad re can cause it to fail.

73-

self.do_find(0)

114+

self.do_find(False)

7411575116

def _replace_expand(self, m, repl):

76-

""" Helper function for expanding a regular expression

77-

in the replace field, if needed. """

117+

"Expand replacement text if regular expression."

78118

if self.engine.isre():

79119

try:

80120

new = m.expand(repl)

@@ -87,7 +127,15 @@ def _replace_expand(self, m, repl):

87127

return new

8812889129

def replace_all(self, event=None):

90-

"""Replace all instances of patvar with replvar in text"""

130+

"""Handle the Replace All button.

131+132+

Search text for occurrences of the Find value and replace

133+

each of them. The 'wrap around' value controls the start

134+

point for searching. If wrap isn't set, then the searching

135+

starts at the first occurrence after the current selection;

136+

if wrap is set, the replacement starts at the first line.

137+

The replacement is always done top-to-bottom in the text.

138+

"""

91139

prog = self.engine.getprog()

92140

if not prog:

93141

return

@@ -104,12 +152,13 @@ def replace_all(self, event=None):

104152

if self.engine.iswrap():

105153

line = 1

106154

col = 0

107-

ok = 1

155+

ok = True

108156

first = last = None

109157

# XXX ought to replace circular instead of top-to-bottom when wrapping

110158

text.undo_block_start()

111-

while 1:

112-

res = self.engine.search_forward(text, prog, line, col, 0, ok)

159+

while True:

160+

res = self.engine.search_forward(text, prog, line, col,

161+

wrap=False, ok=ok)

113162

if not res:

114163

break

115164

line, m = res

@@ -130,13 +179,17 @@ def replace_all(self, event=None):

130179

if new:

131180

text.insert(first, new)

132181

col = i + len(new)

133-

ok = 0

182+

ok = False

134183

text.undo_block_stop()

135184

if first and last:

136185

self.show_hit(first, last)

137186

self.close()

138187139-

def do_find(self, ok=0):

188+

def do_find(self, ok=False):

189+

"""Search for and highlight next occurrence of pattern in text.

190+191+

No text replacement is done with this option.

192+

"""

140193

if not self.engine.getprog():

141194

return False

142195

text = self.text

@@ -149,10 +202,11 @@ def do_find(self, ok=0):

149202

first = "%d.%d" % (line, i)

150203

last = "%d.%d" % (line, j)

151204

self.show_hit(first, last)

152-

self.ok = 1

205+

self.ok = True

153206

return True

154207155208

def do_replace(self):

209+

"Replace search pattern in text with replacement value."

156210

prog = self.engine.getprog()

157211

if not prog:

158212

return False

@@ -180,12 +234,20 @@ def do_replace(self):

180234

text.insert(first, new)

181235

text.undo_block_stop()

182236

self.show_hit(first, text.index("insert"))

183-

self.ok = 0

237+

self.ok = False

184238

return True

185239186240

def show_hit(self, first, last):

187-

"""Highlight text from 'first' to 'last'.

188-

'first', 'last' - Text indices"""

241+

"""Highlight text between first and last indices.

242+243+

Text is highlighted via the 'hit' tag and the marked

244+

section is brought into view.

245+246+

The colors from the 'hit' tag aren't currently shown

247+

when the text is displayed. This is due to the 'sel'

248+

tag being added first, so the colors in the 'sel'

249+

config are seen instead of the colors for 'hit'.

250+

"""

189251

text = self.text

190252

text.mark_set("insert", first)

191253

text.tag_remove("sel", "1.0", "end")

@@ -199,6 +261,7 @@ def show_hit(self, first, last):

199261

text.update_idletasks()

200262201263

def close(self, event=None):

264+

"Close the dialog and remove hit tags."

202265

SearchDialogBase.close(self, event)

203266

self.text.tag_remove("hit", "1.0", "end")

204267