~ubuntu-branches/ubuntu/utopic/pida/utopic

« back to all changes in this revision

Viewing changes to pida/services/grepper.py

  • Committer: Bazaar Package Importer
  • Author(s): Jan Luebbe
  • Date: 2007-04-17 16:08:06 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20070417160806-3ttlb6igf94x9i03
Tags: 0.4.4-1
* New upstream release (closes: #419129)
* Add dependency on python-glade2 (closes: #418716)
* Update copyright

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
 
24
24
 
25
25
import pida.core.service as service
26
 
import pida.core.registry as registry
27
 
import pida.pidagtk.contentbook as contentbook
28
 
import pida.pidagtk.contentview as contentview
29
 
import pida.pidagtk.filedialogs as filedialogs
30
 
import pida.pidagtk.tree as tree
 
26
from  pida.pidagtk import contentview, filedialogs, tree
31
27
 
32
28
#import pida.utils.pygrep as pygrep
33
 
 
34
29
import os
35
30
import re
36
 
import time
37
31
import gobject
38
32
import threading
39
33
import linecache
40
 
import cgi
41
34
import string
42
35
import gtk
43
 
 
44
 
import pida.core.actions as actions
45
 
 
46
 
types = service.types
 
36
from gtk import gdk
 
37
 
 
38
from pida.core import actions
 
39
from pida.utils.gthreads import locked, gcall
 
40
from gobject import markup_escape_text
 
41
 
 
42
from pida.model import attrtypes as types
47
43
defs = service.definitions
48
44
 
49
45
table = range(0,128) * 2
70
66
class ResultsTreeItem(tree.TreeItem):
71
67
    
72
68
    def get_markup(self):
73
 
        return RESULT_MU % (self.value.linenumber,
74
 
                            cgi.escape(self.value.filename),
 
69
        return RESULT_MU % (self.value.linenumber + 1,
 
70
                            markup_escape_text(self.value.filename),
75
71
                            self.value.muline)
76
72
    markup = property(get_markup)
77
73
 
159
155
    def cb_result_selected(self, tree, result):
160
156
        #lines = self.boss.option('grepper', 'context-lines')
161
157
        lines = 4
162
 
        pre, match, post = [cgi.escape(s) for s in
 
158
        pre, match, post = [markup_escape_text(s) for s in
163
159
                            result.value.get_context(lines)]
164
160
        self.__context_label.set_markup(RESULT_CONTEXT_MU % (pre, match, post))
165
161
 
213
209
    def cb_activated(self, entry):
214
210
        self.service.grep_start()
215
211
 
216
 
class Grepper(service.service):
217
 
 
218
 
    single_view_type = GrepView
219
 
    single_view_book = 'view'
220
 
 
221
 
    display_name = 'Grep Search'
222
 
 
 
212
class GrepConfig:
 
213
    __order__  = ['default_options', 'results']
223
214
    class default_options(defs.optiongroup):
224
215
        """Options that the search will start with by default."""
 
216
        __order__ = ['start_detailed', 'recursive_search',
 
217
                     'ignore_version_control_directories']
 
218
        label = 'Default Search Options'
225
219
        class start_detailed(defs.option):
226
220
            """Whether the detailed search options will start expanded."""
 
221
            label = 'Start with details visible'
227
222
            rtype = types.boolean
228
223
            default = False
229
224
        class recursive_search(defs.option):
230
225
            """Whether the search will be recursive by default."""
231
226
            rtype = types.boolean
232
227
            default = True
 
228
            label = 'Recursive search'
233
229
        class ignore_version_control_directories(defs.option):
234
230
            """Whether version control directories will be ignored by default."""
235
231
            rtype = types.boolean
236
232
            default = True
 
233
            label = 'Ignore version control directories'
237
234
    class results(defs.optiongroup):
238
235
        """Options relating to search results."""
 
236
        __order__ = ['maximum_results']
 
237
        label = 'Result Options'
239
238
        class maximum_results(defs.option):
240
239
            """The maximum number of search results."""
 
240
            label = 'Maximum number of results'
241
241
            rtype = types.intrange(5, 5000, 5)
242
242
            default = 500
243
243
 
 
244
    __markup__ = lambda self: 'Text Searcher'
 
245
 
 
246
class Grepper(service.service):
 
247
 
 
248
    config_definition = GrepConfig
 
249
 
 
250
    class GrepView(defs.View):
 
251
        view_type = GrepView
 
252
        book_name = 'view'
 
253
 
 
254
    display_name = 'Grep Search'
 
255
 
244
256
    def grep_start(self):
245
257
        opts = self.single_view.get_options()
246
258
        if not opts.pattern:
261
273
 
262
274
    def cb_results_status(self, grep, status):
263
275
        self.single_view.show_status(status)
264
 
            
265
276
 
266
277
    def cmd_find_interactive(self, directories=None, ignorevcs=None,
267
278
                             recursive=None):
268
 
        self.create_single_view()
 
279
        
 
280
        opts = self.options.default_options
 
281
        view = self.create_view('GrepView')
269
282
        options = GrepOptions()
 
283
 
 
284
        self.show_view(view=view)
 
285
 
270
286
        if directories is None:
271
287
            proj = self.boss.call_command('projectmanager',
272
288
                                          'get_current_project')
 
289
 
273
290
            if proj is not None:
274
 
                options.directories = [proj.source_directory]
 
291
                options.directories = [proj.source__directory]
275
292
            else:
276
293
                options.directories = [os.getcwd()]
277
294
        else:
278
295
            options.directories = directories
279
296
        if ignorevcs is None:
280
 
            options.ignorevcs = self.opt(
281
 
                'default_options', 'ignore_version_control_directories')
 
297
            options.ignorevcs = opts.ignore_version_control_directories
282
298
        else:
283
299
            options.ignorevcs = ignorevcs
284
300
        if recursive is None:
285
 
            options.recursive = self.opt(
286
 
                'default_options', 'recursive_search')
 
301
            options.recursive = opts.recursive_search
287
302
        else:
288
303
            options.recursive = recursive
289
 
        options.maxresults = self.opt('results', 'maximum_results')
 
304
        options.maxresults = self.opts.results__maximum_results
290
305
        self.single_view.from_options(options)
291
 
        self.single_view.set_details_expanded(self.opt(
292
 
           'default_options', 'start_detailed'))
 
306
        self.single_view.set_details_expanded(opts.start_detailed)
293
307
 
294
308
    def cmd_find(self, path, pattern):
295
309
        pass
296
310
 
297
 
 
298
311
    def cb_search_clicked(self, button):
299
312
        self.cmd_find_interactive()
300
313
 
301
314
    def cb_view_action(self, view, name):
302
315
        if name == 'apply':
303
316
            self.grep()
304
 
        if name == 'stop':
 
317
        elif name == 'stop':
305
318
            self.__grep.stop()
306
319
 
307
320
    @actions.action(stock_id='gtk-searchtool',
334
347
                <placeholder name="ToolsToolbar">
335
348
            <separator />
336
349
            <toolitem  name="grepper" action="grepper+find" />
337
 
            <separator />
338
350
                </placeholder>
339
351
                </toolbar>
340
352
            """
 
353
 
 
354
    def get_single_view(self):
 
355
        return self.get_first_view('GrepView')
 
356
    single_view = property(get_single_view)
341
357
    
342
358
    
343
359
BINARY_RE = re.compile(r'[\000-\010\013\014\016-\037\200-\377]')
360
376
        muline = ''
361
377
        for match in matches:
362
378
            prematch, line = line.split(match, 1)
363
 
            muline = '%s%s' % (muline, cgi.escape(prematch))
364
 
            muline = '%s%s' % (muline, MATCH_MU % cgi.escape(match))
365
 
        self.muline = '%s%s' % (muline, cgi.escape(line))
 
379
            muline = '%s%s' % (muline, markup_escape_text(prematch))
 
380
            muline = '%s%s' % (muline, MATCH_MU % markup_escape_text(match))
 
381
        self.muline = '%s%s' % (muline, markup_escape_text(line))
366
382
 
367
383
    def get_context(self, lines=2):
368
384
        pre = []
379
395
 
380
396
    def get_markup(self):
381
397
        return RESULT_MU % (self.linenumber,
382
 
                            cgi.escape(self.filename),
 
398
                            markup_escape_text(self.filename),
383
399
                            self.muline)
384
400
    markup = property(get_markup)
385
401
 
386
402
class PidaGrep(gobject.GObject):
387
 
    
 
403
    """An object that contains two signals, 'found' and 'status',
 
404
    used to monitor its state. This object greps through a group of
 
405
    files."""
 
406
 
388
407
    __gsignals__ = {'found' : (
389
408
                        gobject.SIGNAL_RUN_LAST,
390
409
                        gobject.TYPE_NONE,
400
419
        self.__pattern = re.compile(options.pattern)
401
420
        self.__t = None
402
421
        self.__running = False
403
 
 
 
422
        self.lock = threading.Lock()
 
423
 
 
424
    running = property(lambda x: x.__running,
 
425
                       doc="Returns if this operation is running")
 
426
 
 
427
    @locked("lock")
404
428
    def run(self):
405
 
        self.__t = threading.Thread(target = self.__run)
406
 
        self.__t.start()
 
429
        if self.running:
 
430
            return
407
431
        self.__running = True
 
432
        threading.Thread(target=self._run).start()
408
433
 
409
 
    def get_files(self):
 
434
    def _get_files(self):
410
435
        for directory in self.__options.directories:
411
436
            for dirname, dirnames, filenames in os.walk(directory):
412
437
                if not self.__options.recursive:
413
438
                    dirnames[0:] = []
414
439
                else:
415
440
                    if self.__options.ignorevcs:
416
 
                        for d in dirnames:
417
 
                            if d in ['.svn', 'CVS', '_darcs']:
418
 
                                dirnames.remove(d)
 
441
                        for vcs_dir in ['.svn', 'CVS', '_darcs', '.bzr']:
 
442
                            try:
 
443
                                dirnames.remove(vcs_dir)
 
444
                            except ValueError:
 
445
                                pass
 
446
 
419
447
                for filename in filenames:
420
448
                    filepath = os.path.join(dirname, filename)
421
449
                    yield filepath
422
450
        for filepath in self.__options.files:
423
451
                yield filepath
424
452
 
425
 
    def __run(self):
426
 
        try:
427
 
            self.__find()
428
 
        except StopIteration:
429
 
            return
430
 
 
431
 
    def __find(self):
432
 
        self.status(1, "searching")
433
 
        self.__nfound = 0
434
 
        for i, filename in enumerate(self.get_files()):
 
453
    def _find_files(self):
 
454
        self._status(1, "searching")
 
455
        found = 0
 
456
        
 
457
        for i, filename in enumerate(self._get_files()):
 
458
            if not self.running or found >= self.__options.maxresults:
 
459
                break
 
460
            
 
461
            # update status from time to time
435
462
            if i % 16 == 0:
436
 
                self.status(self.__nfound)
 
463
                self._status(found)
 
464
                
437
465
            try:
438
466
                f = open(filename, 'r')
439
467
            except IOError:
440
468
                continue
 
469
 
441
470
            for linenumber, line in enumerate(f):
442
 
                if not self.__running:
443
 
                    self.__finished()
 
471
                if not self.running:
 
472
                    break
 
473
                
 
474
                # abort binary files
444
475
                if BINARY_RE.match(line):
445
476
                    break
 
477
                    
446
478
                line = string.translate(line, all_chars, hi_bit_chars)
447
479
                line = string.translate(line, hi_lo_table)
448
480
                matches = self.__pattern.findall(line)
 
481
                
449
482
                if len(matches):
450
 
                    self.__nfound = self.__nfound + len(matches)
451
 
                    if self.__nfound >= self.__options.maxresults:
452
 
                        self.__finished()
 
483
                    found = found + len(matches)
 
484
                    
 
485
                    # break when we have found enough values
 
486
                    if found >= self.__options.maxresults:
 
487
                        break
 
488
                    
 
489
                    # emit the result we've found
453
490
                    result = GrepResult(linenumber, filename, line, matches)
454
 
                    gtk.threads_enter()
455
 
                    self.emit('found', result)
456
 
                    gtk.threads_leave()
 
491
                    gcall(self.emit, 'found', result)
 
492
                    
457
493
            f.close()
458
 
        self.__finished()
459
 
 
460
 
    def status(self, code, message=None):
461
 
        gtk.threads_enter()
462
 
        self.emit('status', (code, message))
463
 
        gtk.threads_leave()
464
 
 
 
494
            
 
495
                
 
496
        return found
 
497
 
 
498
    def _run(self):
 
499
        self._status(1, '%s found' % self._find_files())
 
500
 
 
501
        self.lock.acquire()
 
502
        self.__running = False
 
503
        self.lock.release()
 
504
        
 
505
    def _status(self, code, message=None):
 
506
        gcall(self.emit, 'status', (code, message))
 
507
 
 
508
    @locked("lock")
465
509
    def stop(self):
 
510
        """Use this method to stop this operation"""
466
511
        self.__running = False
467
512
 
468
 
    def __finished(self):
469
 
        self.status(1, '%s found' % self.__nfound)
470
 
        raise StopIteration
471
 
 
472
513
gobject.type_register(PidaGrep)
473
514
 
474
515
Service = Grepper