Skip to content

Commit

Permalink
Merge from 3.x: PR #4537
Browse files Browse the repository at this point in the history
Fixes #4490
  • Loading branch information
ccordoba12 committed Jun 3, 2017
2 parents 4a7e4fa + 7e49d73 commit 2a04203
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 62 deletions.
2 changes: 1 addition & 1 deletion spyder/utils/external/binaryornot/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def is_binary(filename):
logger.debug('is_binary: %(filename)r', locals())

# Check if the file extension is in a list of known binary types
binary_extensions = ['pyc', 'iso']
binary_extensions = ['pyc', 'iso', 'zip', 'pdf']
for ext in binary_extensions:
if filename.endswith(ext):
return True
Expand Down
228 changes: 167 additions & 61 deletions spyder/widgets/findinfiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@

# Third party imports
from qtpy.compat import getexistingdirectory
from qtpy.QtCore import QMutex, QMutexLocker, Qt, QThread, Signal, Slot
from qtpy.QtGui import QAbstractTextDocumentLayout, QTextDocument
from qtpy.QtCore import QMutex, QMutexLocker, Qt, QThread, Signal, Slot, QSize
from qtpy.QtWidgets import (QHBoxLayout, QLabel, QRadioButton, QSizePolicy,
QTreeWidgetItem, QVBoxLayout, QWidget,
QHeaderView)
QStyledItemDelegate, QStyleOptionViewItem,
QApplication, QStyle)

# Local imports
from spyder.config.base import _
Expand All @@ -37,6 +39,7 @@
from spyder.widgets.comboboxes import PatternComboBox
from spyder.widgets.onecolumntree import OneColumnTree

from spyder.config.gui import get_font
from spyder.widgets.waitingspinner import QWaitingSpinner


Expand All @@ -45,7 +48,7 @@ class SearchThread(QThread):
sig_finished = Signal(bool)
sig_current_file = Signal(str)
sig_current_folder = Signal(str)
sig_file_match = Signal(dict, int)
sig_file_match = Signal(tuple, int)
sig_out_print = Signal(object)

def __init__(self, parent):
Expand Down Expand Up @@ -125,20 +128,48 @@ def find_files_in_path(self, path):

def truncate_result(self, line, start, end):
ellipsis = '...'
before_offset = 4
if len(line) > 80:
if start <= before_offset:
before_offset = 0
ellipsis = ''
trunc_line = ellipsis + line[start - before_offset:end + 4]
else:
trunc_line = line

left, match, right = line[:start], line[start:end], line[end:]
max_line_length = 40
offset = (len(line) - len(match)) // 2

left = left.split(' ')
num_left_words = len(left)

if num_left_words == 1:
left = left[0]
if len(left) > max_line_length:
left = ellipsis + left[-offset:]
left = [left]

right = right.split(' ')
num_right_words = len(right)

if num_right_words == 1:
right = right[0]
if len(right) > max_line_length:
right = right[:offset] + ellipsis
right = [right]

left = left[-3:]
right = right[:3]

if len(left) < num_left_words:
left = [ellipsis] + left

if len(right) < num_right_words:
right = right + [ellipsis]

left = ' '.join(left)
right = ' '.join(right)

trunc_line = '{0}<b>{1}</b>{2}'.format(left, match, right)
return trunc_line

def find_string_in_file(self, fname):
self.error_flag = False
self.sig_current_file.emit(fname)
results = {}
# results = {}
try:
for lineno, line in enumerate(open(fname, 'rb')):
for text, enc in self.texts:
Expand All @@ -156,28 +187,30 @@ def find_string_in_file(self, fname):
line_dec = line
if self.text_re:
for match in re.finditer(text, line):
res = results.get(osp.abspath(fname), [])
displ_line = self.truncate_result(line_dec,
match.start(),
match.end())
res.append((lineno + 1, match.start(), displ_line))
results[osp.abspath(fname)] = res
self.total_matches += 1
self.sig_file_match.emit((osp.abspath(fname),
lineno + 1,
match.start(), displ_line),
self.total_matches)
else:
found = line.find(text)
while found > -1:
res = results.get(osp.abspath(fname), [])
self.total_matches += 1
displ_line = self.truncate_result(line_dec,
found,
found + len(text))
res.append((lineno + 1, found, displ_line))
results[osp.abspath(fname)] = res

self.sig_file_match.emit((osp.abspath(fname),
lineno + 1,
found, displ_line),
self.total_matches)
for text, enc in self.texts:
found = line.find(text, found + 1)
if found > -1:
break
self.total_matches += 1
if len(results) > 0:
self.sig_file_match.emit(results, self.total_matches)
except IOError as xxx_todo_changeme:
(_errno, _strerror) = xxx_todo_changeme.args
self.error_flag = _("permission denied errors were encountered")
Expand Down Expand Up @@ -231,13 +264,13 @@ def __init__(self, parent, search_text, search_text_regexp, search_path,
self.more_options.setChecked(more_options)

self.ok_button = create_toolbutton(self, text=_("Search"),
icon=ima.icon('DialogApplyButton'),
icon=ima.icon('find'),
triggered=lambda: self.find.emit(),
tip=_("Start search"),
text_beside_icon=True)
self.ok_button.clicked.connect(self.update_combos)
self.stop_button = create_toolbutton(self, text=_("Stop"),
icon=ima.icon('stop'),
icon=ima.icon('editclear'),
triggered=lambda:
self.stop.emit(),
tip=_("Stop search"),
Expand Down Expand Up @@ -267,28 +300,29 @@ def __init__(self, parent, search_text, search_text_regexp, search_path,

# Layout 3
hlayout3 = QHBoxLayout()
search_label = QLabel(_("Search on: "))

self.global_path_search = QRadioButton(_("Current Path"), self)
self.global_path_search = QRadioButton(_("Path"), self)
self.global_path_search.setChecked(True)
self.global_path_search.setToolTip(_("Search in all files and "
"directories present on the"
"current Spyder path"))

self.project_search = QRadioButton(_("Current Project"), self)
self.project_search = QRadioButton(_("Project"), self)
self.project_search.setToolTip(_("Search in all files and "
"directories present on the"
"current project path (If opened)"))

self.project_search.setEnabled(False)

self.file_search = QRadioButton(_("Current File"), self)
self.file_search = QRadioButton(_("File"), self)
self.file_search.setToolTip(_("Search in current opened file"))

for wid in [search_label, self.global_path_search,
for wid in [self.global_path_search,
self.project_search, self.file_search]:
hlayout3.addWidget(wid)

hlayout3.addStretch(1)

self.search_text.valid.connect(lambda valid: self.find.emit())
self.exclude_pattern.valid.connect(lambda valid: self.find.emit())

Expand Down Expand Up @@ -430,9 +464,92 @@ def keyPressEvent(self, event):
QWidget.keyPressEvent(self, event)


class ResultsHeader(QHeaderView):
class LineMatchItem(QTreeWidgetItem):
def __init__(self, parent, lineno, colno, match):
self.lineno = lineno
self.colno = colno
self.match = match
QTreeWidgetItem.__init__(self, parent, [self.__repr__()],
QTreeWidgetItem.Type)

def __repr__(self):
match = to_text_string(self.match).rstrip()
font = get_font()
_str = to_text_string("<b>{1}</b> ({2}): "
"<span style='font-family:{0};"
"font-size:75%;'>{3}</span>")
return _str.format(font.family(), self.lineno, self.colno, match)

def __unicode__(self):
return self.__repr__()

def __str__(self):
return self.__repr__()

def __lt__(self, x):
return self.lineno < x.lineno

def __ge__(self, x):
return self.lineno >= x.lineno


class FileMatchItem(QTreeWidgetItem):
def __init__(self, parent, filename):

self.filename = osp.basename(filename)

title = ('<b>{0}</b><br>'
'<small><em>{1}</em>'
'</small>'.format(osp.basename(filename),
osp.dirname(filename)))
QTreeWidgetItem.__init__(self, parent, [title], QTreeWidgetItem.Type)

self.setToolTip(0, filename)

def __lt__(self, x):
return self.filename < x.filename

def __ge__(self, x):
return self.filename >= x.filename


class ItemDelegate(QStyledItemDelegate):
def __init__(self, parent):
QHeaderView.__init__(self, parent)
QStyledItemDelegate.__init__(self, parent)

def paint(self, painter, option, index):
options = QStyleOptionViewItem(option)
self.initStyleOption(options, index)

style = (QApplication.style() if options.widget is None
else options.widget.style())

doc = QTextDocument()
doc.setDocumentMargin(0)
doc.setHtml(options.text)

options.text = ""
style.drawControl(QStyle.CE_ItemViewItem, options, painter)

ctx = QAbstractTextDocumentLayout.PaintContext()

textRect = style.subElementRect(QStyle.SE_ItemViewItemText, options)
painter.save()

painter.translate(textRect.topLeft())
painter.setClipRect(textRect.translated(-textRect.topLeft()))
doc.documentLayout().draw(painter, ctx)
painter.restore()

def sizeHint(self, option, index):
options = QStyleOptionViewItem(option)
self.initStyleOption(options, index)

doc = QTextDocument()
doc.setHtml(options.text)
doc.setTextWidth(options.rect.width())

return QSize(doc.idealWidth(), doc.size().height())


class ResultsBrowser(OneColumnTree):
Expand All @@ -446,11 +563,14 @@ def __init__(self, parent):
self.error_flag = None
self.completed = None
self.data = None
self.files = None
self.set_title('')
self.root_items = None
self.sortByColumn(0, Qt.AscendingOrder)
self.setSortingEnabled(True)
self.header().setSectionsClickable(True)
# self.setHeaderLabel(_("Filename"))
self.setItemDelegate(ItemDelegate(self))
self.setUniformRowHeights(False)

def activated(self, item):
"""Double-click event"""
Expand All @@ -467,15 +587,22 @@ def clear_title(self, search_text):
self.clear()
self.num_files = 0
self.data = {}
self.files = {}
self.search_text = search_text
title = "'%s' - " % search_text
text = _('String not found')
self.set_title(title + text)

@Slot(dict, int)
@Slot(tuple, int)
def append_result(self, results, num_matches):
"""Real-time update of search results"""
self.num_files += 1
filename, lineno, colno, line = results

if filename not in self.files:
item = FileMatchItem(self, filename)
self.files[filename] = item
self.num_files += 1

search_text = self.search_text
title = "'%s' - " % search_text
nb_files = self.num_files
Expand All @@ -489,21 +616,11 @@ def append_result(self, results, num_matches):
text = "%d %s %d %s" % (num_matches, text_matches,
nb_files, text_files)
self.set_title(title + text)
for filename in sorted(results.keys()):
file_item = QTreeWidgetItem(self, [osp.basename(filename) +
u" - " + osp.dirname(filename)],
QTreeWidgetItem.Type)
file_item.setToolTip(0, filename)
file_item.setIcon(0, get_filetype_icon(filename))
for lineno, colno, line in results[filename]:
item = QTreeWidgetItem(file_item,
[u"{0} ({1}): {2}".format(lineno,
colno,
line.rstrip()
)],
QTreeWidgetItem.Type)
item.setIcon(0, ima.icon('arrow'))
self.data[id(item)] = (filename, lineno, colno)

file_item = self.files[filename]
item = LineMatchItem(file_item, lineno, colno, line)

self.data[id(item)] = (filename, lineno, colno)


class FileProgressBar(QWidget):
Expand Down Expand Up @@ -576,21 +693,9 @@ def __init__(self, parent,

self.result_browser = ResultsBrowser(self)

collapse_btn = create_toolbutton(self)
collapse_btn.setDefaultAction(self.result_browser.collapse_all_action)
expand_btn = create_toolbutton(self)
expand_btn.setDefaultAction(self.result_browser.expand_all_action)
restore_btn = create_toolbutton(self)
restore_btn.setDefaultAction(self.result_browser.restore_action)

btn_layout = QVBoxLayout()
btn_layout.setAlignment(Qt.AlignTop)
for widget in [collapse_btn, expand_btn, restore_btn]:
btn_layout.addWidget(widget)

hlayout = QHBoxLayout()
hlayout.addWidget(self.result_browser)
hlayout.addLayout(btn_layout)
# hlayout.addLayout(btn_layout)

layout = QVBoxLayout()
left, _x, right, bottom = layout.getContentsMargins()
Expand Down Expand Up @@ -656,6 +761,7 @@ def search_complete(self, completed):
self.find_options.ok_button.setEnabled(True)
self.find_options.stop_button.setEnabled(False)
self.status_bar.hide()
self.result_browser.expandAll()
if self.search_thread is None:
return
self.sig_finished.emit()
Expand Down

0 comments on commit 2a04203

Please sign in to comment.