160
178
self.stopped = True
162
180
def find_files_in_python_path(self):
164
directories = [path for path in sys.path \
165
if not path.startswith(sys.prefix)]
166
for path in directories:
169
with QMutexLocker(self.mutex):
172
dirname = osp.dirname(path)
173
if re.search(self.exclude, dirname+os.sep):
175
filename = osp.dirname(path)
176
if re.search(self.exclude, filename):
178
if re.search(self.include, filename):
179
self.filenames.append(path)
181
pathlist = os.environ['PYTHONPATH'].split(os.pathsep)
182
if self.get_pythonpath_callback is not None:
183
pathlist += self.get_pythonpath_callback()
185
# The following avoid doublons on Windows platforms:
186
# (e.g. "d:\Python" in PYTHONPATH environment variable,
187
# and "D:\Python" in Spyder's python path would lead
188
# to two different search folders)
191
for path in pathlist:
192
lcpath = osp.normcase(path)
193
if lcpath not in lcpathlist:
194
lcpathlist.append(lcpath)
195
winpathlist.append(path)
196
pathlist = winpathlist
197
for path in set(pathlist):
199
ok = self.find_files_in_path(path)
182
204
def find_files_in_hg_manifest(self):
183
205
p = Popen(['hg', 'manifest'], stdout=PIPE,
184
206
cwd=self.rootpath, shell=True)
186
207
hgroot = get_hg_root(self.rootpath)
208
self.pathlist = [hgroot]
187
209
for path in p.stdout.read().splitlines():
188
210
with QMutexLocker(self.mutex):
191
213
dirname = osp.dirname(path)
192
if re.search(self.exclude, dirname+os.sep):
194
filename = osp.basename(path)
195
if re.search(self.exclude, filename):
197
if re.search(self.include, filename):
198
self.filenames.append(osp.join(hgroot, path))
201
def find_files_in_path(self):
203
for path, dirs, files in os.walk(self.rootpath):
204
with QMutexLocker(self.mutex):
208
dirname = os.path.join(path, d)
209
215
if re.search(self.exclude, dirname+os.sep):
212
filename = os.path.join(path, f)
217
filename = osp.basename(path)
213
218
if re.search(self.exclude, filename):
215
220
if re.search(self.include, filename):
216
self.filenames.append(filename)
221
self.filenames.append(osp.join(hgroot, path))
223
self.error_flag = translate("FindInFiles",
224
"invalid regular expression")
228
def find_files_in_path(self, path):
229
if self.pathlist is None:
231
self.pathlist.append(path)
232
for path, dirs, files in os.walk(path):
233
with QMutexLocker(self.mutex):
238
dirname = os.path.join(path, d)
239
if re.search(self.exclude, dirname+os.sep):
242
filename = os.path.join(path, f)
243
if re.search(self.exclude, filename):
245
if re.search(self.include, filename):
246
self.filenames.append(filename)
248
self.error_flag = translate("FindInFiles",
249
"invalid regular expression")
219
253
def find_string_in_files(self):
265
302
Find widget with options
267
304
def __init__(self, parent, search_text, search_text_regexp, search_path,
268
include, include_regexp, exclude, exclude_regexp,
269
supported_encodings):
305
include, include_idx, include_regexp,
306
exclude, exclude_idx, exclude_regexp,
307
supported_encodings, in_python_path, more_options):
270
308
QWidget.__init__(self, parent)
272
310
if search_path is None:
287
325
hlayout1 = QHBoxLayout()
288
326
self.search_text = PatternComboBox(self, search_text,
289
327
translate('FindInFiles', "Search pattern"))
290
search_label = QLabel(translate('FindInFiles', "Search text:"))
291
search_label.setBuddy(self.search_text)
292
328
self.edit_regexp = create_toolbutton(self, get_icon("advanced.png"),
293
329
tip=translate('FindInFiles', "Regular expression"))
294
330
self.edit_regexp.setCheckable(True)
295
331
self.edit_regexp.setChecked(search_text_regexp)
332
self.more_widgets = ()
333
self.more_options = create_toolbutton(self,
334
toggled=self.toggle_more_options)
335
self.more_options.setCheckable(True)
336
self.more_options.setChecked(more_options)
296
338
self.ok_button = create_toolbutton(self,
297
339
text=translate('FindInFiles', "Search"),
298
340
triggered=lambda: self.emit(SIGNAL('find()')),
305
347
icon=get_icon("terminate.png"),
306
348
tip=translate('FindInFiles', "Stop search"))
307
349
self.stop_button.setEnabled(False)
308
for widget in [search_label, self.search_text, self.edit_regexp,
309
self.ok_button, self.stop_button]:
350
for widget in [self.search_text, self.edit_regexp,
351
self.ok_button, self.stop_button, self.more_options]:
310
352
hlayout1.addWidget(widget)
313
355
hlayout2 = QHBoxLayout()
314
356
self.include_pattern = PatternComboBox(self, include,
315
357
translate('FindInFiles', "Included filenames pattern"))
358
if include_idx is not None and include_idx >= 0 \
359
and include_idx < self.include_pattern.count():
360
self.include_pattern.setCurrentIndex(include_idx)
316
361
self.include_regexp = create_toolbutton(self, get_icon("advanced.png"),
317
362
tip=translate('FindInFiles',
318
363
"Regular expression"))
348
396
self.detect_hg_repository()
349
397
self.hg_manifest.setToolTip(translate('FindInFiles',
350
398
"Search in current directory hg repository"))
351
searchin_label.setBuddy(self.hg_manifest)
352
self.custom_dir = QRadioButton(translate('FindInFiles',
354
self.custom_dir.setChecked(True)
399
self.custom_dir = QRadioButton(translate('FindInFiles', "Here:"), self)
400
self.custom_dir.setChecked(not in_python_path)
355
401
self.dir_combo = PathComboBox(self)
356
402
self.dir_combo.addItems(search_path)
357
403
self.dir_combo.setToolTip(translate('FindInFiles',
366
412
tip=translate('FindInFiles',
367
413
'Browse a search directory'),
368
414
triggered=self.select_directory)
369
for widget in [searchin_label, self.python_path, self.hg_manifest,
370
self.custom_dir, self.dir_combo, browse]:
415
for widget in [self.python_path, self.hg_manifest, self.custom_dir,
416
self.dir_combo, browse]:
371
417
hlayout3.addWidget(widget)
419
self.connect(self.search_text, SIGNAL("valid(bool)"),
420
lambda valid: self.emit(SIGNAL('find()')))
421
self.connect(self.include_pattern, SIGNAL("valid(bool)"),
422
lambda valid: self.emit(SIGNAL('find()')))
423
self.connect(self.exclude_pattern, SIGNAL("valid(bool)"),
424
lambda valid: self.emit(SIGNAL('find()')))
425
self.connect(self.dir_combo, SIGNAL("valid(bool)"),
426
lambda valid: self.emit(SIGNAL('find()')))
373
428
vlayout = QVBoxLayout()
374
429
vlayout.addLayout(hlayout1)
375
430
vlayout.addLayout(hlayout2)
376
431
vlayout.addLayout(hlayout3)
432
self.more_widgets = (hlayout2, hlayout3)
433
self.toggle_more_options(more_options)
377
434
self.setLayout(vlayout)
379
436
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
438
def toggle_more_options(self, state):
439
for layout in self.more_widgets:
440
for index in range(layout.count()):
441
layout.itemAt(index).widget().setVisible(state)
443
icon_name = 'options_less.png'
444
tip = translate('FindInFiles', 'Hide advanced options')
446
icon_name = 'options_more.png'
447
tip = translate('FindInFiles', 'Show advanced options')
448
self.more_options.setIcon(get_icon(icon_name))
449
self.more_options.setToolTip(tip)
381
451
def update_combos(self):
382
452
self.search_text.lineEdit().emit(SIGNAL('returnPressed()'))
383
453
self.include_pattern.lineEdit().emit(SIGNAL('returnPressed()'))
432
502
for index in range(self.dir_combo.count())]
433
503
include = [unicode(self.include_pattern.itemText(index)) \
434
504
for index in range(self.include_pattern.count())]
505
include_idx = self.include_pattern.currentIndex()
435
506
exclude = [unicode(self.exclude_pattern.itemText(index)) \
436
507
for index in range(self.exclude_pattern.count())]
508
exclude_idx = self.exclude_pattern.currentIndex()
509
more_options = self.more_options.isChecked()
437
510
return (search_text, text_re, search_path,
511
include, include_idx, include_re,
512
exclude, exclude_idx, exclude_re,
513
python_path, more_options)
441
515
return (path, python_path, hg_manifest,
442
516
include, exclude, texts, text_re)
474
548
class ResultsBrowser(OneColumnTree):
475
549
def __init__(self, parent):
476
550
OneColumnTree.__init__(self, parent)
551
self.search_text = None
477
552
self.results = None
479
554
self.error_flag = None
555
self.completed = None
481
557
self.set_title('')
482
self.root_item = None
558
self.root_items = None
484
560
def activated(self):
485
561
itemdata = self.data.get(self.currentItem())
486
562
if itemdata is not None:
487
563
filename, lineno = itemdata
488
self.parent().emit(SIGNAL("edit_goto(QString,int)"),
564
self.parent().emit(SIGNAL("edit_goto(QString,int,QString)"),
565
filename, lineno, self.search_text)
491
def set_results(self, search_text, results, nb, error_flag):
567
def set_results(self, search_text, results, pathlist, nb,
568
error_flag, completed):
492
569
self.search_text = search_text
493
570
self.results = results
571
self.pathlist = pathlist
495
573
self.error_flag = error_flag
574
self.completed = completed
497
576
if not self.error_flag and self.nb:
502
if self.root_item is not None:
503
self.root_item.setExpanded(True)
505
579
def refresh(self):
507
581
Refreshing search results panel
520
594
text_files += 's'
521
595
text = "%d %s %d %s" % (self.nb, text_matches,
522
596
nb_files, text_files)
524
text += ' (' + self.error_flag + ')'
598
text += ' (' + self.error_flag + ')'
599
elif self.results is not None and not self.completed:
600
text += ' (' + translate('FindInFiles', 'interrupted') + ')'
525
601
self.set_title(title+text)
529
if self.results is None: # First search interrupted
605
if not self.results: # First search interrupted *or* No result
610
for filename in sorted(self.results.keys()):
611
dirname = osp.abspath(osp.dirname(filename))
535
for filename in self.results:
536
dirname = osp.dirname(filename)
538
if root_path is None:
615
root_path_list = None
616
_common = get_common_path(list(dir_set))
617
if _common is not None:
618
root_path_list = [_common]
620
_common = get_common_path(self.pathlist)
621
if _common is not None:
622
root_path_list = [_common]
541
while root_path not in dirname:
542
root_path = abspardir(root_path)
543
if root_path is None:
624
root_path_list = self.pathlist
625
if not root_path_list:
545
dir_set.add(root_path)
627
for _root_path in root_path_list:
628
dir_set.add(_root_path)
546
629
# Populating tree: directories
547
630
def create_dir_item(dirname, parent):
548
if dirname != root_path:
631
if dirname not in root_path_list:
549
632
displayed_name = osp.basename(dirname)
551
634
displayed_name = dirname
593
685
def __init__(self, parent,
594
686
search_text = r"# ?TODO|# ?FIXME|# ?XXX",
595
687
search_text_regexp=True, search_path=None,
596
include=".", include_regexp=True,
597
exclude=r"\.pyc$|\.orig$|\.hg|\.svn", exclude_regexp=True,
598
supported_encodings=("utf-8", "iso-8859-1", "cp1252")):
688
include=[".", ".py"], include_idx=None, include_regexp=True,
689
exclude=r"\.pyc$|\.orig$|\.hg|\.svn", exclude_idx=None,
691
supported_encodings=("utf-8", "iso-8859-1", "cp1252"),
692
in_python_path=False, more_options=False):
599
693
QWidget.__init__(self, parent)
695
self.setWindowTitle(translate('FindInFiles', 'Find in files'))
601
697
self.search_thread = SearchThread(self)
602
self.connect(self.search_thread, SIGNAL("finished()"),
698
self.connect(self.search_thread, SIGNAL("finished(bool)"),
603
699
self.search_complete)
605
701
self.find_options = FindOptions(self, search_text, search_text_regexp,
606
search_path, include, include_regexp,
607
exclude, exclude_regexp,
703
include, include_idx, include_regexp,
704
exclude, exclude_idx, exclude_regexp,
705
supported_encodings, in_python_path,
609
707
self.connect(self.find_options, SIGNAL('find()'), self.find)
610
708
self.connect(self.find_options, SIGNAL('stop()'), self.stop)
612
710
self.result_browser = ResultsBrowser(self)
614
collapse_btn = create_toolbutton(self, get_icon("collapse.png"),
615
tip=translate('FindInFiles', "Collapse all"),
616
triggered=self.result_browser.collapseAll)
617
expand_btn = create_toolbutton(self, get_icon("expand.png"),
618
tip=translate('FindInFiles', "Expand all"),
619
triggered=self.result_browser.expandAll)
620
restore_btn = create_toolbutton(self, get_icon("restore.png"),
621
tip=translate('FindInFiles',
622
"Restore original tree layout"),
623
triggered=self.result_browser.restore)
712
collapse_btn = create_toolbutton(self, text_beside_icon=False)
713
collapse_btn.setDefaultAction(self.result_browser.collapse_all_action)
714
expand_btn = create_toolbutton(self, text_beside_icon=False)
715
expand_btn.setDefaultAction(self.result_browser.expand_all_action)
716
restore_btn = create_toolbutton(self, text_beside_icon=False)
717
restore_btn.setDefaultAction(self.result_browser.restore_action)
718
# collapse_sel_btn = create_toolbutton(self, text_beside_icon=False)
719
# collapse_sel_btn.setDefaultAction(
720
# self.result_browser.collapse_selection_action)
721
# expand_sel_btn = create_toolbutton(self, text_beside_icon=False)
722
# expand_sel_btn.setDefaultAction(
723
# self.result_browser.expand_selection_action)
624
725
btn_layout = QVBoxLayout()
625
726
btn_layout.setAlignment(Qt.AlignTop)
626
727
for widget in [collapse_btn, expand_btn, restore_btn]:
728
# collapse_sel_btn, expand_sel_btn]:
627
729
btn_layout.addWidget(widget)
629
731
hlayout = QHBoxLayout()
654
759
if self.search_thread.isRunning():
655
760
self.search_thread.stop()
657
def search_complete(self):
762
def search_complete(self, completed):
658
763
self.find_options.ok_button.setEnabled(True)
659
764
self.find_options.stop_button.setEnabled(False)
660
765
found = self.search_thread.get_results()
661
766
if found is not None:
662
results, nb, error_flag = found
767
results, pathlist, nb, error_flag = found
663
768
search_text = unicode( self.find_options.search_text.currentText() )
664
self.result_browser.set_results(search_text, results,
769
self.result_browser.set_results(search_text, results, pathlist,
770
nb, error_flag, completed)
666
771
self.result_browser.show()
669
if __name__ == '__main__':
670
from PyQt4.QtGui import QApplication
671
app = QApplication([])
775
"""Run Find in Files widget test"""
776
from spyderlib.utils.qthelpers import qapplication
673
778
widget = FindInFilesWidget(None)
676
780
sys.exit(app.exec_())
782
if __name__ == '__main__':
b'\\ No newline at end of file'