Skip to content

Commit

Permalink
Merge from 3.x: PR #4638
Browse files Browse the repository at this point in the history
Fixes #358
  • Loading branch information
ccordoba12 committed Jul 6, 2017
2 parents e456348 + c109aff commit 8f72201
Showing 1 changed file with 112 additions and 42 deletions.
154 changes: 112 additions & 42 deletions spyder/widgets/findreplace.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,13 @@ def __init__(self, parent, enable_replace=False):

self.return_shift_pressed.connect(
lambda:
self.find(changed=False, forward=False, rehighlight=False))
self.find(changed=False, forward=False, rehighlight=False,
multiline_replace_check = False))

self.return_pressed.connect(
lambda:
self.find(changed=False, forward=True, rehighlight=False))
self.find(changed=False, forward=True, rehighlight=False,
multiline_replace_check = False))

self.search_text.lineEdit().textEdited.connect(
self.text_has_been_edited)
Expand Down Expand Up @@ -124,18 +126,29 @@ def __init__(self, parent, enable_replace=False):
self.replace_text.valid.connect(
lambda _: self.replace_find(focus_replace_text=True))
self.replace_button = create_toolbutton(self,
text=_('Replace/find'),
text=_('Replace/find next'),
icon=ima.icon('DialogApplyButton'),
triggered=self.replace_find,
text_beside_icon=True)
self.replace_button.clicked.connect(self.update_replace_combo)
self.replace_button.clicked.connect(self.update_search_combo)

self.all_check = QCheckBox(_("Replace all"))
self.replace_sel_button = create_toolbutton(self,
text=_('Replace selection'),
icon=ima.icon('DialogApplyButton'),
triggered=self.replace_find_selection,
text_beside_icon=True)
self.replace_sel_button.clicked.connect(self.update_replace_combo)
self.replace_sel_button.clicked.connect(self.update_search_combo)

self.replace_all_button = create_toolbutton(self,
text=_('Replace all'),
icon=ima.icon('DialogApplyButton'),
triggered=self.replace_find_all,
text_beside_icon=True)
self.replace_all_button.clicked.connect(self.update_replace_combo)
self.replace_all_button.clicked.connect(self.update_search_combo)

self.replace_layout = QHBoxLayout()
widgets = [replace_with, self.replace_text, self.replace_button,
self.all_check]
self.replace_sel_button, self.replace_all_button]
for widget in widgets:
self.replace_layout.addWidget(widget)
glayout.addLayout(self.replace_layout, 1, 1)
Expand Down Expand Up @@ -184,7 +197,7 @@ def create_shortcuts(self, parent):
name='Find previous', parent=parent)
togglefind = config_shortcut(self.show, context='_',
name='Find text', parent=parent)
togglereplace = config_shortcut(self.toggle_replace_widgets,
togglereplace = config_shortcut(self.show_replace,
context='_', name='Replace text',
parent=parent)
hide = config_shortcut(self.hide, context='_', name='hide find and replace',
Expand Down Expand Up @@ -215,7 +228,8 @@ def toggle_replace_widgets(self):
self.hide()
else:
self.show_replace()
self.replace_text.setFocus()
if len(to_text_string(self.search_text.currentText()))>0:
self.replace_text.setFocus()

@Slot(bool)
def toggle_highlighting(self, state):
Expand All @@ -226,32 +240,38 @@ def toggle_highlighting(self, state):
else:
self.clear_matches()

def show(self):
def show(self, hide_replace=True):
"""Overrides Qt Method"""
QWidget.show(self)
self.visibility_changed.emit(True)
if self.editor is not None:
if hide_replace:
if self.replace_widgets[0].isVisible():
self.hide_replace()
text = self.editor.get_selected_text()
highlighted = True
# If no text is highlighted for search, use whatever word is under
# the cursor
if not text:
highlighted = False
try:
cursor = self.editor.textCursor()
cursor.select(QTextCursor.WordUnderCursor)
text = to_text_string(cursor.selectedText())
except AttributeError:
# We can't do this for all widgets, e.g. WebView's
pass

# Now that text value is sorted out, use it for the search
if text and not self.search_text.currentText() or highlighted:
self.search_text.setEditText(text)
self.search_text.lineEdit().selectAll()
self.refresh()
else:
self.search_text.lineEdit().selectAll()
# When selecting several lines, and replace box is activated the
# text won't be replaced for the selection
if hide_replace or len(text.splitlines())<=1:
highlighted = True
# If no text is highlighted for search, use whatever word is
# under the cursor
if not text:
highlighted = False
try:
cursor = self.editor.textCursor()
cursor.select(QTextCursor.WordUnderCursor)
text = to_text_string(cursor.selectedText())
except AttributeError:
# We can't do this for all widgets, e.g. WebView's
pass

# Now that text value is sorted out, use it for the search
if text and not self.search_text.currentText() or highlighted:
self.search_text.setEditText(text)
self.search_text.lineEdit().selectAll()
self.refresh()
else:
self.search_text.lineEdit().selectAll()
self.search_text.setFocus()

@Slot()
Expand All @@ -267,7 +287,7 @@ def hide(self):

def show_replace(self):
"""Show replace widgets"""
self.show()
self.show(hide_replace=False)
for widget in self.replace_widgets:
widget.show()

Expand Down Expand Up @@ -314,23 +334,25 @@ def set_editor(self, editor, refresh=True):
@Slot()
def find_next(self):
"""Find next occurrence"""
state = self.find(changed=False, forward=True, rehighlight=False)
state = self.find(changed=False, forward=True, rehighlight=False,
multiline_replace_check=False)
self.editor.setFocus()
self.search_text.add_current_text()
return state

@Slot()
def find_previous(self):
"""Find previous occurrence"""
state = self.find(changed=False, forward=False, rehighlight=False)
state = self.find(changed=False, forward=False, rehighlight=False,
multiline_replace_check=False)
self.editor.setFocus()
return state

def text_has_been_edited(self, text):
"""Find text has been edited (this slot won't be triggered when
setting the search pattern combo box text programmatically"""
setting the search pattern combo box text programmatically)"""
self.find(changed=True, forward=True, start_highlight_timer=True)

def highlight_matches(self):
"""Highlight found results"""
if self.is_code_editor and self.highlight_button.isChecked():
Expand All @@ -339,15 +361,21 @@ def highlight_matches(self):
regexp = self.re_button.isChecked()
self.editor.highlight_found_results(text, words=words,
regexp=regexp)

def clear_matches(self):
"""Clear all highlighted matches"""
if self.is_code_editor:
self.editor.clear_found_results()

def find(self, changed=True, forward=True,
rehighlight=True, start_highlight_timer=False):
rehighlight=True, start_highlight_timer=False, multiline_replace_check=True):
"""Call the find function"""
# When several lines are selected in the editor and replace box is activated,
# dynamic search is deactivated to prevent changing the selection. Otherwise
# we show matching items.
if multiline_replace_check and self.replace_widgets[0].isVisible() and \
len(to_text_string(self.editor.get_selected_text()).splitlines())>1:
return None
text = self.search_text.currentText()
if len(text) == 0:
self.search_text.lineEdit().setStyleSheet("")
Expand All @@ -374,7 +402,7 @@ def find(self, changed=True, forward=True,
return found

@Slot()
def replace_find(self, focus_replace_text=False):
def replace_find(self, focus_replace_text=False, replace_all=False):
"""Replace and find"""
if (self.editor is not None):
replace_text = to_text_string(self.replace_text.currentText())
Expand Down Expand Up @@ -444,10 +472,52 @@ def replace_find(self, focus_replace_text=False):
QTextCursor.KeepAnchor)
else:
break
if not self.all_check.isChecked():
if not replace_all:
break
self.all_check.setCheckState(Qt.Unchecked)
if cursor is not None:
cursor.endEditBlock()
if focus_replace_text:
self.replace_text.setFocus()

@Slot()
def replace_find_all(self, focus_replace_text=False):
"""Replace and find all matching occurrences"""
self.replace_find(focus_replace_text, replace_all=True)


@Slot()
def replace_find_selection(self, focus_replace_text=False):
"""Replace and find in the current selection"""
if (self.editor is not None):
replace_text = to_text_string(self.replace_text.currentText())
search_text = to_text_string(self.search_text.currentText())
pattern = search_text if self.re_button.isChecked() else None
case = self.case_button.isChecked()
words = self.words_button.isChecked()
re_flags = re.MULTILINE if case else re.IGNORECASE|re.MULTILINE

cursor = self.editor.textCursor()
cursor.beginEditBlock()
seltxt = to_text_string(self.editor.get_selected_text())
if not pattern:
pattern = re.escape(search_text)
replace_text = re.escape(replace_text)
if words:
#If whole words is checked we need to check that each match
#is actually a whole word before replacing
try:
re.compile(pattern)
except re.error:
return #if the pattern won't compile cancel the find/replace
word_pattern = r'\b{pattern}\b'.format(pattern = pattern)
replacement = re.sub(word_pattern, replace_text, seltxt, flags=re_flags)
else:
replacement = re.sub(pattern, replace_text, seltxt, flags=re_flags)
if replacement != seltxt:
cursor.removeSelectedText()
cursor.insertText(replacement)
cursor.endEditBlock()
if focus_replace_text:
self.replace_text.setFocus()
else:
self.editor.setFocus()

0 comments on commit 8f72201

Please sign in to comment.