Exemplo n.º 1
0
class FindInFilesDialog(QDialog, object):
    """ find in files dialog implementation """

    inProject = 0
    inDirectory = 1
    inOpenFiles = 2

    def __init__(self, where, what="", dirPath="", filters=[], parent=None):

        QDialog.__init__(self, parent)

        mainWindow = GlobalData().mainWindow
        self.editorsManager = mainWindow.editorsManagerWidget.editorsManager

        self.__cancelRequest = False
        self.__inProgress = False
        self.searchRegexp = None
        self.searchResults = []

        # Avoid pylint complains
        self.findCombo = None
        self.caseCheckBox = None
        self.wordCheckBox = None
        self.regexpCheckBox = None
        self.projectRButton = None
        self.openFilesRButton = None
        self.dirRButton = None
        self.dirEditCombo = None
        self.dirSelectButton = None
        self.filterCombo = None
        self.fileLabel = None
        self.progressBar = None
        self.findButton = None

        self.__createLayout()
        self.setWindowTitle("Find in files")

        # Restore the combo box values
        project = GlobalData().project
        if project.fileName != "":
            self.findFilesWhat = project.findFilesWhat
            self.findFilesDirs = project.findFilesDirs
            self.findFilesMasks = project.findFilesMasks
        else:
            settings = Settings()
            self.findFilesWhat = settings.findFilesWhat
            self.findFilesDirs = settings.findFilesDirs
            self.findFilesMasks = settings.findFilesMasks
        self.findCombo.addItems(self.findFilesWhat)
        self.findCombo.setEditText("")
        self.dirEditCombo.addItems(self.findFilesDirs)
        self.dirEditCombo.setEditText("")
        self.filterCombo.addItems(self.findFilesMasks)
        self.filterCombo.setEditText("")

        if where == self.inProject:
            self.setSearchInProject(what, filters)
        elif where == self.inDirectory:
            self.setSearchInDirectory(what, dirPath, filters)
        else:
            self.setSearchInOpenFiles(what, filters)

        return

    def __createLayout(self):
        """ Creates the dialog layout """

        self.resize(600, 300)
        self.setSizeGripEnabled(True)

        verticalLayout = QVBoxLayout(self)
        gridLayout = QGridLayout()

        # Combo box for the text to search
        findLabel = QLabel(self)
        findLabel.setText("Find text:")
        self.findCombo = QComboBox(self)
        self.__tuneCombo(self.findCombo)
        self.findCombo.lineEdit().setToolTip(
            "Regular expression to search for")
        self.findCombo.editTextChanged.connect(self.__someTextChanged)

        gridLayout.addWidget(findLabel, 0, 0, 1, 1)
        gridLayout.addWidget(self.findCombo, 0, 1, 1, 1)
        verticalLayout.addLayout(gridLayout)

        # Check boxes
        horizontalCBLayout = QHBoxLayout()
        self.caseCheckBox = QCheckBox(self)
        self.caseCheckBox.setText("Match &case")
        horizontalCBLayout.addWidget(self.caseCheckBox)
        self.wordCheckBox = QCheckBox(self)
        self.wordCheckBox.setText("Match whole &word")
        horizontalCBLayout.addWidget(self.wordCheckBox)
        self.regexpCheckBox = QCheckBox(self)
        self.regexpCheckBox.setText("Regular &expression")
        horizontalCBLayout.addWidget(self.regexpCheckBox)

        verticalLayout.addLayout(horizontalCBLayout)

        # Files groupbox
        filesGroupbox = QGroupBox(self)
        filesGroupbox.setTitle("Find in")
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            filesGroupbox.sizePolicy().hasHeightForWidth())
        filesGroupbox.setSizePolicy(sizePolicy)

        gridLayoutFG = QGridLayout(filesGroupbox)
        self.projectRButton = QRadioButton(filesGroupbox)
        self.projectRButton.setText("&Project")
        gridLayoutFG.addWidget(self.projectRButton, 0, 0)
        self.projectRButton.clicked.connect(self.__projectClicked)

        self.openFilesRButton = QRadioButton(filesGroupbox)
        self.openFilesRButton.setText("&Opened files only")
        gridLayoutFG.addWidget(self.openFilesRButton, 1, 0)
        self.openFilesRButton.clicked.connect(self.__openFilesOnlyClicked)

        self.dirRButton = QRadioButton(filesGroupbox)
        self.dirRButton.setText("&Directory tree")
        gridLayoutFG.addWidget(self.dirRButton, 2, 0)
        self.dirRButton.clicked.connect(self.__dirClicked)

        self.dirEditCombo = QComboBox(filesGroupbox)
        self.__tuneCombo(self.dirEditCombo)
        self.dirEditCombo.lineEdit().setToolTip("Directory to search in")
        gridLayoutFG.addWidget(self.dirEditCombo, 2, 1)
        self.dirEditCombo.editTextChanged.connect(self.__someTextChanged)

        self.dirSelectButton = QPushButton(filesGroupbox)
        self.dirSelectButton.setText("...")
        gridLayoutFG.addWidget(self.dirSelectButton, 2, 2)
        self.dirSelectButton.clicked.connect(self.__selectDirClicked)

        filterLabel = QLabel(filesGroupbox)
        filterLabel.setText("Files filter:")
        gridLayoutFG.addWidget(filterLabel, 3, 0)
        self.filterCombo = QComboBox(filesGroupbox)
        self.__tuneCombo(self.filterCombo)
        self.filterCombo.lineEdit().setToolTip("File names regular expression")
        gridLayoutFG.addWidget(self.filterCombo, 3, 1)
        self.filterCombo.editTextChanged.connect(self.__someTextChanged)

        verticalLayout.addWidget(filesGroupbox)

        # File label
        self.fileLabel = FitPathLabel(self)
        self.fileLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)
        verticalLayout.addWidget(self.fileLabel)

        # Progress bar
        self.progressBar = QProgressBar(self)
        self.progressBar.setValue(0)
        self.progressBar.setOrientation(Qt.Horizontal)
        verticalLayout.addWidget(self.progressBar)

        # Buttons at the bottom
        buttonBox = QDialogButtonBox(self)
        buttonBox.setOrientation(Qt.Horizontal)
        buttonBox.setStandardButtons(QDialogButtonBox.Cancel)
        self.findButton = buttonBox.addButton("Find",
                                              QDialogButtonBox.AcceptRole)
        self.findButton.setDefault(True)
        self.findButton.clicked.connect(self.__process)
        verticalLayout.addWidget(buttonBox)

        buttonBox.rejected.connect(self.__onClose)
        return

    @staticmethod
    def __tuneCombo(comboBox):
        " Sets the common settings for a combo box "
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth( \
                            comboBox.sizePolicy().hasHeightForWidth() )
        comboBox.setSizePolicy(sizePolicy)
        comboBox.setEditable(True)
        comboBox.setInsertPolicy(QComboBox.InsertAtTop)
        comboBox.setAutoCompletion(False)
        comboBox.setDuplicatesEnabled(False)
        return

    def __onClose(self):
        " Triggered when the close button is clicked "

        self.__cancelRequest = True
        if not self.__inProgress:
            self.close()
        return

    def setSearchInProject(self, what="", filters=[]):
        " Set search ready for the whole project "

        if GlobalData().project.fileName == "":
            # No project loaded, fallback to opened files
            self.setSearchInOpenFiles(what, filters)
            return

        # Select the project radio button
        self.projectRButton.setEnabled(True)
        self.projectRButton.setChecked(True)
        self.dirEditCombo.setEnabled(False)
        self.dirSelectButton.setEnabled(False)

        openedFiles = self.editorsManager.getTextEditors()
        self.openFilesRButton.setEnabled(len(openedFiles) != 0)

        self.setFilters(filters)

        self.findCombo.setEditText(what)
        self.findCombo.lineEdit().selectAll()
        self.findCombo.setFocus()

        # Check searchability
        self.__testSearchability()
        return

    def setSearchInOpenFiles(self, what="", filters=[]):
        " Sets search ready for the opened files "

        openedFiles = self.editorsManager.getTextEditors()
        if len(openedFiles) == 0:
            # No opened files, fallback to search in dir
            self.setSearchInDirectory(what, "", filters)
            return

        # Select the radio buttons
        self.projectRButton.setEnabled(GlobalData().project.fileName != "")
        self.openFilesRButton.setEnabled(True)
        self.openFilesRButton.setChecked(True)
        self.dirEditCombo.setEnabled(False)
        self.dirSelectButton.setEnabled(False)

        self.setFilters(filters)

        self.findCombo.setEditText(what)
        self.findCombo.lineEdit().selectAll()
        self.findCombo.setFocus()

        # Check searchability
        self.__testSearchability()
        return

    def setSearchInDirectory(self, what="", dirPath="", filters=[]):
        " Sets search ready for the given directory "

        # Select radio buttons
        self.projectRButton.setEnabled(GlobalData().project.fileName != "")
        openedFiles = self.editorsManager.getTextEditors()
        self.openFilesRButton.setEnabled(len(openedFiles) != 0)
        self.dirRButton.setEnabled(True)
        self.dirRButton.setChecked(True)
        self.dirEditCombo.setEnabled(True)
        self.dirSelectButton.setEnabled(True)
        self.dirEditCombo.setEditText(dirPath)

        self.setFilters(filters)

        self.findCombo.setEditText(what)
        self.findCombo.lineEdit().selectAll()
        self.findCombo.setFocus()

        # Check searchability
        self.__testSearchability()
        return

    def setFilters(self, filters):
        " Sets up the filters "

        # Set filters if provided
        if filters:
            self.filterCombo.setEditText(";".join(filters))
        else:
            self.filterCombo.setEditText("")
        return

    def __testSearchability(self):
        " Tests the searchability and sets the Find button status "

        startTime = time.time()
        if self.findCombo.currentText().strip() == "":
            self.findButton.setEnabled(False)
            self.findButton.setToolTip("No text to search")
            return

        if self.dirRButton.isChecked():
            dirname = self.dirEditCombo.currentText().strip()
            if dirname == "":
                self.findButton.setEnabled(False)
                self.findButton.setToolTip("No directory path")
                return
            if not os.path.isdir(dirname):
                self.findButton.setEnabled(False)
                self.findButton.setToolTip("Path is not a directory")
                return

        # Now we need to match file names if there is a filter
        filtersText = self.filterCombo.currentText().strip()
        if filtersText == "":
            self.findButton.setEnabled(True)
            self.findButton.setToolTip("Find in files")
            return

        # Need to check the files match
        try:
            filterRe = re.compile(filtersText, re.IGNORECASE)
        except:
            self.findButton.setEnabled(False)
            self.findButton.setToolTip( "Incorrect files " \
                                        "filter regular expression" )
            return

        matched = False
        tooLong = False
        if self.projectRButton.isChecked():
            # Whole project
            for fname in GlobalData().project.filesList:
                if fname.endswith(sep):
                    continue
                matched = filterRe.match(fname)
                if matched:
                    break
                # Check the time, it might took too long
                if time.time() - startTime > 0.1:
                    tooLong = True
                    break

        elif self.openFilesRButton.isChecked():
            # Opened files
            openedFiles = self.editorsManager.getTextEditors()
            for record in openedFiles:
                matched = filterRe.match(record[1])
                if matched:
                    break
                # Check the time, it might took too long
                if time.time() - startTime > 0.1:
                    tooLong = True
                    break

        else:
            # Search in the dir
            if not dirname.endswith(sep):
                dirname += sep
            matched, tooLong = self.__matchInDir(dirname, filterRe, startTime)

        if matched:
            self.findButton.setEnabled(True)
            self.findButton.setToolTip("Find in files")
        else:
            if tooLong:
                self.findButton.setEnabled(True)
                self.findButton.setToolTip("Find in files")
            else:
                self.findButton.setEnabled(False)
                self.findButton.setToolTip("No files matched to search in")
        return

    @staticmethod
    def __matchInDir(path, filterRe, startTime):
        " Provides the 'match' and 'too long' statuses "
        matched = False
        tooLong = False
        for item in os.listdir(path):
            if time.time() - startTime > 0.1:
                tooLong = True
                return matched, tooLong
            if os.path.isdir(path + item):
                dname = path + item + sep
                matched, tooLong = FindInFilesDialog.__matchInDir(
                    dname, filterRe, startTime)
                if matched or tooLong:
                    return matched, tooLong
                continue
            if filterRe.match(path + item):
                matched = True
                return matched, tooLong
        return matched, tooLong

    def __projectClicked(self):
        " project radio button clicked "
        self.dirEditCombo.setEnabled(False)
        self.dirSelectButton.setEnabled(False)
        self.__testSearchability()
        return

    def __openFilesOnlyClicked(self):
        " open files only radio button clicked "
        self.dirEditCombo.setEnabled(False)
        self.dirSelectButton.setEnabled(False)
        self.__testSearchability()
        return

    def __dirClicked(self):
        " dir radio button clicked "
        self.dirEditCombo.setEnabled(True)
        self.dirSelectButton.setEnabled(True)
        self.dirEditCombo.setFocus()
        self.__testSearchability()
        return

    def __someTextChanged(self, text):
        " Text to search, filter or dir name has been changed "
        self.__testSearchability()
        return

    def __selectDirClicked(self):
        " The user selects a directory "
        dirName = QFileDialog.getExistingDirectory(
            self, "Select directory to search in",
            self.dirEditCombo.currentText(),
            QFileDialog.Options(QFileDialog.ShowDirsOnly))

        if dirName:
            self.dirEditCombo.setEditText(os.path.normpath(dirName))
        self.__testSearchability()
        return

    def __projectFiles(self, filterRe):
        " Project files list respecting the mask "
        mainWindow = GlobalData().mainWindow
        files = []
        for fname in GlobalData().project.filesList:
            if fname.endswith(sep):
                continue
            if filterRe is None or filterRe.match(fname):
                widget = mainWindow.getWidgetForFileName(fname)
                if widget is None:
                    # Do not check for broken symlinks
                    if isFileSearchable(fname, False):
                        files.append(ItemToSearchIn(fname, ""))
                else:
                    if widget.getType() in \
                                [ MainWindowTabWidgetBase.PlainTextEditor ]:
                        files.append(ItemToSearchIn(fname, widget.getUUID()))
            QApplication.processEvents()
            if self.__cancelRequest:
                raise Exception("Cancel request")
        return files

    def __openedFiles(self, filterRe):
        " Currently opened editor buffers "

        files = []
        openedFiles = self.editorsManager.getTextEditors()
        for record in openedFiles:
            uuid = record[0]
            fname = record[1]
            if filterRe is None or filterRe.match(fname):
                files.append(ItemToSearchIn(fname, uuid))
            QApplication.processEvents()
            if self.__cancelRequest:
                raise Exception("Cancel request")
        return files

    def __dirFiles(self, path, filterRe, files):
        " Files recursively for the dir "
        for item in os.listdir(path):
            QApplication.processEvents()
            if self.__cancelRequest:
                raise Exception("Cancel request")
            if os.path.isdir(path + item):
                if item in [".svn", ".cvs"]:
                    # It does not make sense to search in revision control dirs
                    continue
                anotherDir, isLoop = resolveLink(path + item)
                if not isLoop:
                    self.__dirFiles(anotherDir + sep, filterRe, files)
                continue
            if not os.path.isfile(path + item):
                continue
            realItem, isLoop = resolveLink(path + item)
            if isLoop:
                continue
            if filterRe is None or filterRe.match(realItem):
                found = False
                for itm in files:
                    if itm.fileName == realItem:
                        found = True
                        break
                if not found:
                    mainWindow = GlobalData().mainWindow
                    widget = mainWindow.getWidgetForFileName(realItem)
                    if widget is None:
                        if isFileSearchable(realItem):
                            files.append(ItemToSearchIn(realItem, ""))
                    else:
                        if widget.getType() in \
                                    [ MainWindowTabWidgetBase.PlainTextEditor ]:
                            files.append(
                                ItemToSearchIn(realItem, widget.getUUID()))
        return

    def __buildFilesList(self):
        " Builds the list of files to search in "
        filtersText = self.filterCombo.currentText().strip()
        if filtersText != "":
            filterRe = re.compile(filtersText, re.IGNORECASE)
        else:
            filterRe = None

        if self.projectRButton.isChecked():
            return self.__projectFiles(filterRe)

        if self.openFilesRButton.isChecked():
            return self.__openedFiles(filterRe)

        dirname = os.path.realpath(self.dirEditCombo.currentText().strip())
        files = []
        self.__dirFiles(dirname + sep, filterRe, files)
        return files

    def __process(self):
        " Search process "

        # Add entries to the combo box if required
        regexpText = self.findCombo.currentText()
        if regexpText in self.findFilesWhat:
            self.findFilesWhat.remove(regexpText)
        self.findFilesWhat.insert(0, regexpText)
        if len(self.findFilesWhat) > 32:
            self.findFilesWhat = self.findFilesWhat[:32]
        self.findCombo.clear()
        self.findCombo.addItems(self.findFilesWhat)

        filtersText = self.filterCombo.currentText().strip()
        if filtersText in self.findFilesMasks:
            self.findFilesMasks.remove(filtersText)
        self.findFilesMasks.insert(0, filtersText)
        if len(self.findFilesMasks) > 32:
            self.findFilesMasks = self.findFilesMasks[:32]
        self.filterCombo.clear()
        self.filterCombo.addItems(self.findFilesMasks)

        if self.dirRButton.isChecked():
            dirText = self.dirEditCombo.currentText().strip()
            if dirText in self.findFilesDirs:
                self.findFilesDirs.remove(dirText)
            self.findFilesDirs.insert(0, dirText)
            if len(self.findFilesDirs) > 32:
                self.findFilesDirs = self.findFilesDirs[:32]
            self.dirEditCombo.clear()
            self.dirEditCombo.addItems(self.findFilesDirs)

        # Save the combo values for further usage
        if GlobalData().project.fileName != "":
            GlobalData().project.setFindInFilesHistory(self.findFilesWhat,
                                                       self.findFilesDirs,
                                                       self.findFilesMasks)
        else:
            Settings().findFilesWhat = self.findFilesWhat
            Settings().findFilesDirs = self.findFilesDirs
            Settings().findFilesMasks = self.findFilesMasks

        self.__inProgress = True
        numberOfMatches = 0
        self.searchResults = []
        self.searchRegexp = None

        # Form the regexp to search
        if not self.regexpCheckBox.isChecked():
            regexpText = re.escape(regexpText)
        if self.wordCheckBox.isChecked():
            regexpText = "\\b%s\\b" % regexpText
        flags = re.UNICODE | re.LOCALE
        if not self.caseCheckBox.isChecked():
            flags |= re.IGNORECASE

        try:
            self.searchRegexp = re.compile(regexpText, flags)
        except:
            logging.error("Invalid search expression")
            self.close()
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        self.fileLabel.setPath('Building list of files to search in...')
        QApplication.processEvents()
        try:
            files = self.__buildFilesList()
        except Exception, exc:
            if "Cancel request" in str(exc):
                QApplication.restoreOverrideCursor()
                self.close()
                return
            else:
                QApplication.restoreOverrideCursor()
                logging.error(str(exc))
                self.close()
                return
        QApplication.restoreOverrideCursor()
        QApplication.processEvents()

        if len(files) == 0:
            self.fileLabel.setPath('No files to search in')
            return

        self.progressBar.setRange(0, len(files))

        index = 1
        for item in files:

            if self.__cancelRequest:
                self.__inProgress = False
                self.close()
                return

            self.fileLabel.setPath( 'Matches: ' + str( numberOfMatches ) + \
                                    ' Processing: ' + item.fileName )

            item.search(self.searchRegexp)
            found = len(item.matches)
            if found > 0:
                numberOfMatches += found
                self.searchResults.append(item)

            self.progressBar.setValue(index)
            index += 1

            QApplication.processEvents()

        if numberOfMatches == 0:
            if len(files) == 1:
                self.fileLabel.setPath("No matches in 1 file.")
            else:
                self.fileLabel.setPath( "No matches in " + \
                                        str( len( files ) ) + " files." )
            self.__inProgress = False
        else:
            self.close()
        return
Exemplo n.º 2
0
class LineCounterDialog( QDialog, object ):
    """ line counter dialog implementation """

    def __init__( self, fName = None, editor = None, parent = None ):

        QDialog.__init__( self, parent )

        self.__cancelRequest = False
        self.__inProgress = False
        if fName is not None:
            self.__fName = fName.strip()
        else:
            self.__fName = None
        self.__editor = editor
        self.__createLayout()
        self.setWindowTitle( "Line counter" )
        QTimer.singleShot( 0, self.__process )
        return

    def __createLayout( self ):
        """ Creates the dialog layout """

        self.resize( 450, 220 )
        self.setSizeGripEnabled( True )

        self.verticalLayout = QVBoxLayout( self )

        # Info label
        self.infoLabel = FitPathLabel( self )
        #sizePolicy = QSizePolicy( QSizePolicy.Expanding,
        #                          QSizePolicy.Preferred )
        sizePolicy = QSizePolicy( QSizePolicy.Minimum,
                                  QSizePolicy.Preferred )
        sizePolicy.setHorizontalStretch( 0 )
        sizePolicy.setVerticalStretch( 0 )
        sizePolicy.setHeightForWidth(
                    self.infoLabel.sizePolicy().hasHeightForWidth() )
        self.infoLabel.setSizePolicy( sizePolicy )
        self.verticalLayout.addWidget( self.infoLabel )

        # Progress bar
        self.progressBar = QProgressBar( self )
        self.progressBar.setValue( 0 )
        self.progressBar.setOrientation( Qt.Horizontal )
        self.verticalLayout.addWidget( self.progressBar )

        # Result window
        self.resultEdit = QTextEdit( self )
        self.resultEdit.setTabChangesFocus( False )
        self.resultEdit.setAcceptRichText( False )
        self.resultEdit.setReadOnly( True )
        self.resultEdit.setFontFamily( GlobalData().skin.baseMonoFontFace )
        font = self.resultEdit.font()

        # Calculate the vertical size
        fontMetrics = QFontMetrics( font )
        rect = fontMetrics.boundingRect( "W" )
        # 6 lines, 5 line spacings, 2 frames
        self.resultEdit.setMinimumHeight( rect.height() * 7 + 4 * 5 +
                                          self.resultEdit.frameWidth() * 2 )
        self.verticalLayout.addWidget( self.resultEdit )

        # Buttons
        self.buttonBox = QDialogButtonBox( self )
        self.buttonBox.setOrientation( Qt.Horizontal )
        self.buttonBox.setStandardButtons( QDialogButtonBox.Close )
        self.verticalLayout.addWidget( self.buttonBox )

        self.buttonBox.rejected.connect( self.__onClose )
        return

    def __scanDir( self, path, files ):
        " Recursively builds a list of python files "

        if path in self.__scannedDirs:
            return

        self.__scannedDirs.append( path )
        for item in os.listdir( path ):
            if os.path.isdir( path + item ):
                nestedDir = os.path.realpath( path + item )
                if not nestedDir.endswith( os.path.sep ):
                    nestedDir += os.path.sep
                self.__scanDir( nestedDir, files )
            else:
                candidate = os.path.realpath( path + item )
                if candidate.endswith( ".py" ) or \
                   candidate.endswith( ".py3" ) or \
                   candidate.endswith( ".pyw" ):
                    if not candidate in files:
                        files.append( candidate )
        return

    def __onClose( self ):
        " triggered when the close button is clicked "

        self.__cancelRequest = True
        if not self.__inProgress:
            self.close()
        return

    def __process( self ):
        " Accumulation process "

        self.__inProgress = True
        if self.__fName is not None:
            if os.path.exists( self.__fName ):
                if os.path.isdir( self.__fName ):
                    self.__fName = os.path.realpath( self.__fName )
                    if not self.__fName.endswith( os.path.sep ):
                        self.__fName += os.path.sep
                    files = []
                    self.__scannedDirs = []
                    QApplication.setOverrideCursor( QCursor( Qt.WaitCursor ) )
                    self.__scanDir( self.__fName, files )
                    QApplication.restoreOverrideCursor()
                else:
                    files = [ self.__fName ]
            else:
                files = []
        elif self.__editor is not None:
            files = [ "buffer" ]
        else:
            files = GlobalData().project.filesList
        self.progressBar.setRange( 0, len( files ) )

        accumulator = LinesCounter()
        current = LinesCounter()

        index = 1
        for fileName in files:

            if self.__cancelRequest:
                self.__inProgress = False
                self.close()
                return

            self.infoLabel.setPath( 'Processing: ' + fileName )

            processed = False
            if self.__editor is not None:
                current.getLinesInBuffer( self.__editor )
                processed = True
            else:
                if (fileName.endswith( '.py' ) or \
                    fileName.endswith( '.py3' ) or \
                    fileName.endswith( '.pyw' )) and \
                   os.path.exists( fileName ):
                    current.getLines( fileName )
                    processed = True

            if processed:
                accumulator.files += current.files
                accumulator.filesSize += current.filesSize
                accumulator.codeLines += current.codeLines
                accumulator.emptyLines += current.emptyLines
                accumulator.commentLines += current.commentLines
                accumulator.classes += current.classes

            self.progressBar.setValue( index )
            index += 1

            QApplication.processEvents()

        self.infoLabel.setPath( 'Done' )
        self.__inProgress = False

        # Update text in the text window
        nFiles = splitThousands( str( accumulator.files ) )
        filesSize = splitThousands( str( accumulator.filesSize ) )
        classes = splitThousands( str( accumulator.classes ) )
        codeLines = splitThousands( str( accumulator.codeLines ) )
        emptyLines = splitThousands( str( accumulator.emptyLines ) )
        commentLines = splitThousands( str( accumulator.commentLines ) )
        totalLines = splitThousands( str( accumulator.codeLines +
                                          accumulator.emptyLines +
                                          accumulator.commentLines ) )

        output = "Classes:                 " + classes + "\n" \
                 "Code lines:              " + codeLines + "\n" \
                 "Empty lines:             " + emptyLines + "\n" \
                 "Comment lines:           " + commentLines + "\n" \
                 "Total lines:             " + totalLines

        if self.__editor is None:
            output = "Number of python files:  " + nFiles + "\n" \
                     "Total files size:        " + filesSize + " bytes\n" + \
                     output
        else:
            output = "Number of characters:    " + filesSize + "\n" + \
                     output

        self.resultEdit.setText( output )
        return
Exemplo n.º 3
0
class FindInFilesDialog( QDialog, object ):
    """ find in files dialog implementation """

    inProject = 0
    inDirectory = 1
    inOpenFiles = 2

    def __init__( self, where, what = "",
                  dirPath = "", filters = [],
                  parent = None ):

        QDialog.__init__( self, parent )

        mainWindow = GlobalData().mainWindow
        self.editorsManager = mainWindow.editorsManagerWidget.editorsManager

        self.__cancelRequest = False
        self.__inProgress = False
        self.searchRegexp = None
        self.searchResults = []

        # Avoid pylint complains
        self.findCombo = None
        self.caseCheckBox = None
        self.wordCheckBox = None
        self.regexpCheckBox = None
        self.projectRButton = None
        self.openFilesRButton = None
        self.dirRButton = None
        self.dirEditCombo = None
        self.dirSelectButton = None
        self.filterCombo = None
        self.fileLabel = None
        self.progressBar = None
        self.findButton = None

        self.__createLayout()
        self.setWindowTitle( "Find in files" )

        # Restore the combo box values
        project = GlobalData().project
        if project.fileName != "":
            self.findFilesWhat = project.findFilesWhat
            self.findFilesDirs = project.findFilesDirs
            self.findFilesMasks = project.findFilesMasks
        else:
            settings = Settings()
            self.findFilesWhat = settings.findFilesWhat
            self.findFilesDirs = settings.findFilesDirs
            self.findFilesMasks = settings.findFilesMasks
        self.findCombo.addItems( self.findFilesWhat )
        self.findCombo.setEditText( "" )
        self.dirEditCombo.addItems( self.findFilesDirs )
        self.dirEditCombo.setEditText( "" )
        self.filterCombo.addItems( self.findFilesMasks )
        self.filterCombo.setEditText( "" )

        if where == self.inProject:
            self.setSearchInProject( what, filters )
        elif where == self.inDirectory:
            self.setSearchInDirectory( what, dirPath, filters )
        else:
            self.setSearchInOpenFiles( what, filters )

        return

    def __createLayout( self ):
        """ Creates the dialog layout """

        self.resize( 600, 300 )
        self.setSizeGripEnabled( True )

        verticalLayout = QVBoxLayout( self )
        gridLayout = QGridLayout()

        # Combo box for the text to search
        findLabel = QLabel( self )
        findLabel.setText( "Find text:" )
        self.findCombo = QComboBox( self )
        self.__tuneCombo( self.findCombo )
        self.findCombo.lineEdit().setToolTip( "Regular expression to search for" )
        self.findCombo.editTextChanged.connect( self.__someTextChanged )

        gridLayout.addWidget( findLabel, 0, 0, 1, 1 )
        gridLayout.addWidget( self.findCombo, 0, 1, 1, 1 )
        verticalLayout.addLayout( gridLayout )

        # Check boxes
        horizontalCBLayout = QHBoxLayout()
        self.caseCheckBox = QCheckBox( self )
        self.caseCheckBox.setText( "Match &case" )
        horizontalCBLayout.addWidget( self.caseCheckBox )
        self.wordCheckBox = QCheckBox( self )
        self.wordCheckBox.setText( "Match whole &word" )
        horizontalCBLayout.addWidget( self.wordCheckBox )
        self.regexpCheckBox = QCheckBox( self )
        self.regexpCheckBox.setText( "Regular &expression" )
        horizontalCBLayout.addWidget( self.regexpCheckBox )

        verticalLayout.addLayout( horizontalCBLayout )

        # Files groupbox
        filesGroupbox = QGroupBox( self )
        filesGroupbox.setTitle( "Find in" )
        sizePolicy = QSizePolicy( QSizePolicy.Expanding, QSizePolicy.Preferred )
        sizePolicy.setHorizontalStretch( 0 )
        sizePolicy.setVerticalStretch( 0 )
        sizePolicy.setHeightForWidth(
                        filesGroupbox.sizePolicy().hasHeightForWidth() )
        filesGroupbox.setSizePolicy( sizePolicy )

        gridLayoutFG = QGridLayout( filesGroupbox )
        self.projectRButton = QRadioButton( filesGroupbox )
        self.projectRButton.setText( "&Project" )
        gridLayoutFG.addWidget( self.projectRButton, 0, 0 )
        self.projectRButton.clicked.connect( self.__projectClicked )

        self.openFilesRButton = QRadioButton( filesGroupbox )
        self.openFilesRButton.setText( "&Opened files only" )
        gridLayoutFG.addWidget( self.openFilesRButton, 1, 0 )
        self.openFilesRButton.clicked.connect( self.__openFilesOnlyClicked )

        self.dirRButton = QRadioButton( filesGroupbox )
        self.dirRButton.setText( "&Directory tree" )
        gridLayoutFG.addWidget( self.dirRButton, 2, 0 )
        self.dirRButton.clicked.connect( self.__dirClicked )

        self.dirEditCombo = QComboBox( filesGroupbox )
        self.__tuneCombo( self.dirEditCombo )
        self.dirEditCombo.lineEdit().setToolTip( "Directory to search in" )
        gridLayoutFG.addWidget( self.dirEditCombo, 2, 1 )
        self.dirEditCombo.editTextChanged.connect( self.__someTextChanged )

        self.dirSelectButton = QPushButton( filesGroupbox )
        self.dirSelectButton.setText( "..." )
        gridLayoutFG.addWidget( self.dirSelectButton, 2, 2 )
        self.dirSelectButton.clicked.connect( self.__selectDirClicked )

        filterLabel = QLabel( filesGroupbox )
        filterLabel.setText( "Files filter:" )
        gridLayoutFG.addWidget( filterLabel, 3, 0 )
        self.filterCombo = QComboBox( filesGroupbox )
        self.__tuneCombo( self.filterCombo )
        self.filterCombo.lineEdit().setToolTip( "File names regular expression" )
        gridLayoutFG.addWidget( self.filterCombo, 3, 1 )
        self.filterCombo.editTextChanged.connect( self.__someTextChanged )

        verticalLayout.addWidget( filesGroupbox )

        # File label
        self.fileLabel = FitPathLabel( self )
        self.fileLabel.setSizePolicy( QSizePolicy.Ignored, QSizePolicy.Fixed )
        verticalLayout.addWidget( self.fileLabel )

        # Progress bar
        self.progressBar = QProgressBar( self )
        self.progressBar.setValue( 0 )
        self.progressBar.setOrientation( Qt.Horizontal )
        verticalLayout.addWidget( self.progressBar )

        # Buttons at the bottom
        buttonBox = QDialogButtonBox( self )
        buttonBox.setOrientation( Qt.Horizontal )
        buttonBox.setStandardButtons( QDialogButtonBox.Cancel )
        self.findButton = buttonBox.addButton( "Find",
                                               QDialogButtonBox.AcceptRole )
        self.findButton.setDefault( True )
        self.findButton.clicked.connect( self.__process )
        verticalLayout.addWidget( buttonBox )

        buttonBox.rejected.connect( self.__onClose )
        return

    @staticmethod
    def __tuneCombo( comboBox ):
        " Sets the common settings for a combo box "
        sizePolicy = QSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed )
        sizePolicy.setHorizontalStretch( 0 )
        sizePolicy.setVerticalStretch( 0 )
        sizePolicy.setHeightForWidth( \
                            comboBox.sizePolicy().hasHeightForWidth() )
        comboBox.setSizePolicy( sizePolicy )
        comboBox.setEditable( True )
        comboBox.setInsertPolicy( QComboBox.InsertAtTop )
        comboBox.setAutoCompletion( False )
        comboBox.setDuplicatesEnabled( False )
        return

    def __onClose( self ):
        " Triggered when the close button is clicked "

        self.__cancelRequest = True
        if not self.__inProgress:
            self.close()
        return

    def setSearchInProject( self, what = "", filters = [] ):
        " Set search ready for the whole project "

        if GlobalData().project.fileName == "":
            # No project loaded, fallback to opened files
            self.setSearchInOpenFiles( what, filters )
            return

        # Select the project radio button
        self.projectRButton.setEnabled( True )
        self.projectRButton.setChecked( True )
        self.dirEditCombo.setEnabled( False )
        self.dirSelectButton.setEnabled( False )

        openedFiles = self.editorsManager.getTextEditors()
        self.openFilesRButton.setEnabled( len( openedFiles ) != 0 )

        self.setFilters( filters )

        self.findCombo.setEditText( what )
        self.findCombo.lineEdit().selectAll()
        self.findCombo.setFocus()

        # Check searchability
        self.__testSearchability()
        return


    def setSearchInOpenFiles( self, what = "", filters = [] ):
        " Sets search ready for the opened files "

        openedFiles = self.editorsManager.getTextEditors()
        if len( openedFiles ) == 0:
            # No opened files, fallback to search in dir
            self.setSearchInDirectory( what, "", filters )
            return

        # Select the radio buttons
        self.projectRButton.setEnabled( GlobalData().project.fileName != "" )
        self.openFilesRButton.setEnabled( True )
        self.openFilesRButton.setChecked( True )
        self.dirEditCombo.setEnabled( False )
        self.dirSelectButton.setEnabled( False )

        self.setFilters( filters )

        self.findCombo.setEditText( what )
        self.findCombo.lineEdit().selectAll()
        self.findCombo.setFocus()

        # Check searchability
        self.__testSearchability()
        return


    def setSearchInDirectory( self, what = "", dirPath = "", filters = [] ):
        " Sets search ready for the given directory "

        # Select radio buttons
        self.projectRButton.setEnabled( GlobalData().project.fileName != "" )
        openedFiles = self.editorsManager.getTextEditors()
        self.openFilesRButton.setEnabled( len( openedFiles ) != 0 )
        self.dirRButton.setEnabled( True )
        self.dirRButton.setChecked( True )
        self.dirEditCombo.setEnabled( True )
        self.dirSelectButton.setEnabled( True )
        self.dirEditCombo.setEditText( dirPath )

        self.setFilters( filters )

        self.findCombo.setEditText( what )
        self.findCombo.lineEdit().selectAll()
        self.findCombo.setFocus()

        # Check searchability
        self.__testSearchability()
        return


    def setFilters( self, filters ):
        " Sets up the filters "

        # Set filters if provided
        if filters:
            self.filterCombo.setEditText( ";".join( filters ) )
        else:
            self.filterCombo.setEditText( "" )
        return

    def __testSearchability( self ):
        " Tests the searchability and sets the Find button status "

        startTime = time.time()
        if self.findCombo.currentText().strip() == "":
            self.findButton.setEnabled( False )
            self.findButton.setToolTip( "No text to search" )
            return

        if self.dirRButton.isChecked():
            dirname = self.dirEditCombo.currentText().strip()
            if dirname == "":
                self.findButton.setEnabled( False )
                self.findButton.setToolTip( "No directory path" )
                return
            if not os.path.isdir( dirname ):
                self.findButton.setEnabled( False )
                self.findButton.setToolTip( "Path is not a directory" )
                return

        # Now we need to match file names if there is a filter
        filtersText = self.filterCombo.currentText().strip()
        if filtersText == "":
            self.findButton.setEnabled( True )
            self.findButton.setToolTip( "Find in files" )
            return

        # Need to check the files match
        try:
            filterRe = re.compile( filtersText, re.IGNORECASE )
        except:
            self.findButton.setEnabled( False )
            self.findButton.setToolTip( "Incorrect files " \
                                        "filter regular expression" )
            return

        matched = False
        tooLong = False
        if self.projectRButton.isChecked():
            # Whole project
            for fname in GlobalData().project.filesList:
                if fname.endswith( sep ):
                    continue
                matched = filterRe.match( fname )
                if matched:
                    break
                # Check the time, it might took too long
                if time.time() - startTime > 0.1:
                    tooLong = True
                    break

        elif self.openFilesRButton.isChecked():
            # Opened files
            openedFiles = self.editorsManager.getTextEditors()
            for record in openedFiles:
                matched = filterRe.match( record[ 1 ] )
                if matched:
                    break
                # Check the time, it might took too long
                if time.time() - startTime > 0.1:
                    tooLong = True
                    break

        else:
            # Search in the dir
            if not dirname.endswith( sep ):
                dirname += sep
            matched, tooLong = self.__matchInDir( dirname, filterRe, startTime )

        if matched:
            self.findButton.setEnabled( True )
            self.findButton.setToolTip( "Find in files" )
        else:
            if tooLong:
                self.findButton.setEnabled( True )
                self.findButton.setToolTip( "Find in files" )
            else:
                self.findButton.setEnabled( False )
                self.findButton.setToolTip( "No files matched to search in" )
        return

    @staticmethod
    def __matchInDir( path, filterRe, startTime ):
        " Provides the 'match' and 'too long' statuses "
        matched = False
        tooLong = False
        for item in os.listdir( path ):
            if time.time() - startTime > 0.1:
                tooLong = True
                return matched, tooLong
            if os.path.isdir( path + item ):
                dname = path + item + sep
                matched, tooLong = FindInFilesDialog.__matchInDir( dname,
                                                                   filterRe,
                                                                   startTime )
                if matched or tooLong:
                    return matched, tooLong
                continue
            if filterRe.match( path + item ):
                matched = True
                return matched, tooLong
        return matched, tooLong

    def __projectClicked( self ):
        " project radio button clicked "
        self.dirEditCombo.setEnabled( False )
        self.dirSelectButton.setEnabled( False )
        self.__testSearchability()
        return

    def __openFilesOnlyClicked( self ):
        " open files only radio button clicked "
        self.dirEditCombo.setEnabled( False )
        self.dirSelectButton.setEnabled( False )
        self.__testSearchability()
        return

    def __dirClicked( self ):
        " dir radio button clicked "
        self.dirEditCombo.setEnabled( True )
        self.dirSelectButton.setEnabled( True )
        self.dirEditCombo.setFocus()
        self.__testSearchability()
        return

    def __someTextChanged( self, text ):
        " Text to search, filter or dir name has been changed "
        self.__testSearchability()
        return

    def __selectDirClicked( self ):
        " The user selects a directory "
        dirName = QFileDialog.getExistingDirectory( self,
                    "Select directory to search in",
                    self.dirEditCombo.currentText(),
                    QFileDialog.Options( QFileDialog.ShowDirsOnly ) )

        if dirName:
            self.dirEditCombo.setEditText( os.path.normpath( dirName ) )
        self.__testSearchability()
        return


    def __projectFiles( self, filterRe ):
        " Project files list respecting the mask "
        mainWindow = GlobalData().mainWindow
        files = []
        for fname in GlobalData().project.filesList:
            if fname.endswith( sep ):
                continue
            if filterRe is None or filterRe.match( fname ):
                widget = mainWindow.getWidgetForFileName( fname )
                if widget is None:
                    # Do not check for broken symlinks
                    if isFileSearchable( fname, False ):
                        files.append( ItemToSearchIn( fname, "" ) )
                else:
                    if widget.getType() in \
                                [ MainWindowTabWidgetBase.PlainTextEditor ]:
                        files.append( ItemToSearchIn( fname,
                                                      widget.getUUID() ) )
            QApplication.processEvents()
            if self.__cancelRequest:
                raise Exception( "Cancel request" )
        return files

    def __openedFiles( self, filterRe ):
        " Currently opened editor buffers "

        files = []
        openedFiles = self.editorsManager.getTextEditors()
        for record in openedFiles:
            uuid = record[ 0 ]
            fname = record[ 1 ]
            if filterRe is None or filterRe.match( fname ):
                files.append( ItemToSearchIn( fname, uuid ) )
            QApplication.processEvents()
            if self.__cancelRequest:
                raise Exception( "Cancel request" )
        return files

    def __dirFiles( self, path, filterRe, files ):
        " Files recursively for the dir "
        for item in os.listdir( path ):
            QApplication.processEvents()
            if self.__cancelRequest:
                raise Exception( "Cancel request" )
            if os.path.isdir( path + item ):
                if item in [ ".svn", ".cvs" ]:
                    # It does not make sense to search in revision control dirs
                    continue
                anotherDir, isLoop = resolveLink( path + item )
                if not isLoop:
                    self.__dirFiles( anotherDir + sep,
                                     filterRe, files )
                continue
            if not os.path.isfile( path + item ):
                continue
            realItem, isLoop = resolveLink( path + item )
            if isLoop:
                continue
            if filterRe is None or filterRe.match( realItem ):
                found = False
                for itm in files:
                    if itm.fileName == realItem:
                        found = True
                        break
                if not found:
                    mainWindow = GlobalData().mainWindow
                    widget = mainWindow.getWidgetForFileName( realItem )
                    if widget is None:
                        if isFileSearchable( realItem ):
                            files.append( ItemToSearchIn( realItem, "" ) )
                    else:
                        if widget.getType() in \
                                    [ MainWindowTabWidgetBase.PlainTextEditor ]:
                            files.append( ItemToSearchIn( realItem,
                                                          widget.getUUID() ) )
        return


    def __buildFilesList( self ):
        " Builds the list of files to search in "
        filtersText = self.filterCombo.currentText().strip()
        if filtersText != "":
            filterRe = re.compile( filtersText, re.IGNORECASE )
        else:
            filterRe = None

        if self.projectRButton.isChecked():
            return self.__projectFiles( filterRe )

        if self.openFilesRButton.isChecked():
            return self.__openedFiles( filterRe )

        dirname = os.path.realpath( self.dirEditCombo.currentText().strip() )
        files = []
        self.__dirFiles( dirname + sep, filterRe, files )
        return files


    def __process( self ):
        " Search process "

        # Add entries to the combo box if required
        regexpText = self.findCombo.currentText()
        if regexpText in self.findFilesWhat:
            self.findFilesWhat.remove( regexpText )
        self.findFilesWhat.insert( 0, regexpText )
        if len( self.findFilesWhat ) > 32:
            self.findFilesWhat = self.findFilesWhat[ : 32 ]
        self.findCombo.clear()
        self.findCombo.addItems( self.findFilesWhat )

        filtersText = self.filterCombo.currentText().strip()
        if filtersText in self.findFilesMasks:
            self.findFilesMasks.remove( filtersText )
        self.findFilesMasks.insert( 0, filtersText )
        if len( self.findFilesMasks ) > 32:
            self.findFilesMasks = self.findFilesMasks[ : 32 ]
        self.filterCombo.clear()
        self.filterCombo.addItems( self.findFilesMasks )

        if self.dirRButton.isChecked():
            dirText = self.dirEditCombo.currentText().strip()
            if dirText in self.findFilesDirs:
                self.findFilesDirs.remove( dirText )
            self.findFilesDirs.insert( 0, dirText )
            if len( self.findFilesDirs ) > 32:
                self.findFilesDirs = self.findFilesDirs[ : 32 ]
            self.dirEditCombo.clear()
            self.dirEditCombo.addItems( self.findFilesDirs )

        # Save the combo values for further usage
        if GlobalData().project.fileName != "":
            GlobalData().project.setFindInFilesHistory( self.findFilesWhat,
                                                        self.findFilesDirs,
                                                        self.findFilesMasks )
        else:
            Settings().findFilesWhat = self.findFilesWhat
            Settings().findFilesDirs = self.findFilesDirs
            Settings().findFilesMasks = self.findFilesMasks


        self.__inProgress = True
        numberOfMatches = 0
        self.searchResults = []
        self.searchRegexp = None

        # Form the regexp to search
        if not self.regexpCheckBox.isChecked():
            regexpText = re.escape( regexpText )
        if self.wordCheckBox.isChecked():
            regexpText = "\\b%s\\b" % regexpText
        flags = re.UNICODE | re.LOCALE
        if not self.caseCheckBox.isChecked():
            flags |= re.IGNORECASE

        try:
            self.searchRegexp = re.compile( regexpText, flags )
        except:
            logging.error( "Invalid search expression" )
            self.close()
            return


        QApplication.setOverrideCursor( QCursor( Qt.WaitCursor ) )
        self.fileLabel.setPath( 'Building list of files to search in...' )
        QApplication.processEvents()
        try:
            files = self.__buildFilesList()
        except Exception, exc:
            if "Cancel request" in str( exc ):
                QApplication.restoreOverrideCursor()
                self.close()
                return
            else:
                QApplication.restoreOverrideCursor()
                logging.error( str( exc ) )
                self.close()
                return
        QApplication.restoreOverrideCursor()
        QApplication.processEvents()

        if len( files ) == 0:
            self.fileLabel.setPath( 'No files to search in' )
            return

        self.progressBar.setRange( 0, len( files ) )

        index = 1
        for item in files:

            if self.__cancelRequest:
                self.__inProgress = False
                self.close()
                return

            self.fileLabel.setPath( 'Matches: ' + str( numberOfMatches ) + \
                                    ' Processing: ' + item.fileName )

            item.search( self.searchRegexp )
            found = len( item.matches )
            if found > 0:
                numberOfMatches += found
                self.searchResults.append( item )

            self.progressBar.setValue( index )
            index += 1

            QApplication.processEvents()

        if numberOfMatches == 0:
            if len( files ) == 1:
                self.fileLabel.setPath( "No matches in 1 file." )
            else:
                self.fileLabel.setPath( "No matches in " + \
                                        str( len( files ) ) + " files." )
            self.__inProgress = False
        else:
            self.close()
        return