Ejemplo n.º 1
0
class Main(plugin.Plugin):
    ' main class for plugin '
    def initialize(self, *args, **kwargs):
        ' class init '
        super(Main, self).initialize(*args, **kwargs)
        self.group0 = QGroupBox()
        self.group0.setTitle(' Options ')
        self.group0.setCheckable(True)
        self.group0.toggled.connect(lambda: self.group0.hide())
        self.spin1, self.spin2, self.spin3 = QSpinBox(), QSpinBox(), QSpinBox()
        self.spin4, self.spin5, self.spin6 = QSpinBox(), QSpinBox(), QSpinBox()
        self.spin7, self.spin8, self.spin9 = QSpinBox(), QSpinBox(), QSpinBox()
        self.spin10, self.output = QSpinBox(), QTextEdit()
        self.reset = QPushButton(QIcon.fromTheme("face-smile"), 'Reset Options')
        self.reset.clicked.connect(self.reset_options)
        self.reset_options()
        self.output.setReadOnly(True)
        vboxg3 = QVBoxLayout(self.group0)
        for each_widget in (QLabel('<b>Max Attributes Per Class:'), self.spin1,
            QLabel('<b>Max Methods Per Class:'), self.spin2,
            QLabel('<b>Max Functions Per File:'), self.spin3,
            QLabel('<b>Max Classes Per File:'), self.spin4,
            QLabel('<b>Max Parameters Per Function:'), self.spin5,
            QLabel('<b>Max Lines Per Function:'), self.spin6,
            QLabel('<b>Max ControlStatements Per Function:'), self.spin7,
            QLabel('<b>Max Lines Per File:'), self.spin8,
            QLabel('<b>Max Indentation Levels:'), self.spin9,
            QLabel('<b>Max Tabs:'), self.spin10, self.reset):
            vboxg3.addWidget(each_widget)

        self.group1, self.auto = QGroupBox(), QComboBox()
        self.group1.setTitle(' Automation ')
        self.group1.setCheckable(True)
        self.group1.setToolTip('<font color="red"><b>WARNING:Advanced Setting!')
        self.group1.toggled.connect(lambda: self.group1.hide())
        self.auto.addItems(['Never run automatically', 'Run when File Saved',
            'Run when File Executed', 'Run when Tab Changed',
            'Run when File Opened', 'Run before File Saved'])
        self.auto.currentIndexChanged.connect(self.on_auto_changed)
        QVBoxLayout(self.group1).addWidget(self.auto)

        self.button = QPushButton(' Analyze for Best Practice ')
        self.button.setMinimumSize(75, 50)
        self.button.clicked.connect(self.run)
        glow = QGraphicsDropShadowEffect(self)
        glow.setOffset(0)
        glow.setBlurRadius(99)
        glow.setColor(QColor(99, 255, 255))
        self.button.setGraphicsEffect(glow)

        class TransientWidget(QWidget):
            ' persistant widget thingy '
            def __init__(self, widget_list):
                ' init sub class '
                super(TransientWidget, self).__init__()
                vbox = QVBoxLayout(self)
                for each_widget in widget_list:
                    vbox.addWidget(each_widget)

        tw = TransientWidget((QLabel('<i>Best Practice analyzer'),
            self.group0, self.group1, QLabel('<b>Best Practice Errors:'),
            self.output, self.button))
        self.scrollable, self.dock = QScrollArea(), QDockWidget()
        self.scrollable.setWidgetResizable(True)
        self.scrollable.setWidget(tw)
        self.dock.setWindowTitle(__doc__)
        self.dock.setStyleSheet('QDockWidget::title{text-align: center;}')
        self.dock.setWidget(self.scrollable)
        ExplorerContainer().addTab(self.dock, "Check")
        QPushButton(QIcon.fromTheme("help-about"), 'About', self.dock
            ).clicked.connect(lambda:
            QMessageBox.information(self.dock, __doc__, HELPMSG))

    def run(self):
        ' run the actions '
        global MAXVAL
        self.output.clear()
        self.button.setDisabled(True)
        maxvalues = {'maxAttributesPerClass': int(self.spin1.value()),
            'maxFunctionsPerClass': int(self.spin2.value()),
            'maxFunctionsPerFile': int(self.spin3.value()),
            'maxClassesPerFile': int(self.spin4.value()),
            'maxParametersPerFunction': int(self.spin5.value()),
            'maxLinesPerFunction': int(self.spin6.value()),
            'maxControlStatementsPerFunction': int(self.spin7.value()),
            'maxLinesPerFile': int(self.spin8.value()),
            'maxIndentationLevel': int(self.spin9.value()),
            'maxTabs': int(self.spin10.value())}
        MAXVAL = maxvalues
        self.output.append(SimplePythonChecker().analyze(str(
            self.locator.get_service("editor").get_opened_documents()[
             self.locator.get_service("editor").get_tab_manager().currentIndex()
            ]), maxvalues))
        self.output.setFocus()
        self.button.setEnabled(True)

    def on_auto_changed(self):
        ' automation connects '
        if self.auto.currentIndex() is 1:
            self.locator.get_service('editor').fileSaved.connect(lambda:
                self.run())
            QMessageBox.information(self.dock, __doc__,
            '<b>Now Actions will Run Automatically when any File is Saved !')
        elif self.auto.currentIndex() is 2:
            self.locator.get_service('editor').fileExecuted.connect(lambda:
                self.run())
            QMessageBox.information(self.dock, __doc__,
            '<b>Now Actions will Run Automatically when any File is Executed !')
        elif self.auto.currentIndex() is 3:
            self.locator.get_service('editor').currentTabChanged.connect(lambda:
                self.run())
            QMessageBox.information(self.dock, __doc__,
            '<b>Now Actions will Run Automatically when current Tab is Changed')
        elif self.auto.currentIndex() is 4:
            self.locator.get_service('editor').fileOpened.connect(lambda:
                self.run())
            QMessageBox.information(self.dock, __doc__,
            '<b>Now Actions will Run Automatically when any File is Opened !')
        elif self.auto.currentIndex() is 5:
            self.locator.get_service('editor').beforeFileSaved.connect(lambda:
                self.run())
            QMessageBox.information(self.dock, __doc__,
            '<b>Now Actions will Run Automatically before any File is Saved !')
        self.group1.setDisabled(True)

    def reset_options(self):
        ' reset the options '
        self.spin1.setRange(1, 99)
        self.spin2.setRange(1, 99)
        self.spin3.setRange(1, 99)
        self.spin4.setRange(1, 99)
        self.spin5.setRange(1, 9)
        self.spin6.setRange(1, 999)
        self.spin7.setRange(1, 99)
        self.spin8.setRange(1, 9999)
        self.spin9.setRange(1, 9)
        self.spin10.setRange(1, 9)
        self.spin1.setValue(20)
        self.spin2.setValue(20)
        self.spin3.setValue(20)
        self.spin4.setValue(5)
        self.spin5.setValue(5)
        self.spin6.setValue(100)
        self.spin7.setValue(20)
        self.spin8.setValue(999)
        self.spin9.setValue(5)
        self.spin10.setValue(5)
Ejemplo n.º 2
0
class Main(plugin.Plugin):
    ' main class for plugin '
    def initialize(self, *args, **kwargs):
        ' class init '
        super(Main, self).initialize(*args, **kwargs)
        self.process = QProcess()
        self.process.readyReadStandardOutput.connect(self.readOutput)
        self.process.readyReadStandardError.connect(self.readErrors)
        self.process.finished.connect(self._process_finished)
        self.process.error.connect(self._process_finished)
        # directory auto completer
        self.completer, self.dirs = QCompleter(self), QDirModel(self)
        self.dirs.setFilter(QDir.Dirs | QDir.NoDotAndDotDot)
        self.completer.setModel(self.dirs)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setCompletionMode(QCompleter.PopupCompletion)

        menu = QMenu('Clones')
        menu.addAction('Analyze for Code Clones here', lambda: self.make_clon())
        self.locator.get_service('explorer').add_project_menu(menu, lang='all')

        self.group1 = QGroupBox()
        self.group1.setTitle(' Target ')
        self.outdir, self.igndir = QLineEdit(path.expanduser("~")), QLineEdit()
        self.outdir.setCompleter(self.completer)
        self.btn1 = QPushButton(QIcon.fromTheme("document-open"), ' Open ')
        self.btn1.clicked.connect(lambda: self.outdir.setText(str(
            QFileDialog.getExistingDirectory(self.dock,
            'Please, Open a Target Directory...', path.expanduser("~")))))
        self.btn1a = QPushButton(QIcon.fromTheme("face-smile"),
                                 'Get from Ninja active project')
        self.btn1a.clicked.connect(lambda: self.outdir.setText(
          self.locator.get_service('explorer').get_current_project_item().path))

        self.ignckb, self.ignmor = QComboBox(), QTextEdit()
        self.ignckb.addItems(['Single Directory', 'Multiple Directories CSV'])
        self.ignckb.currentIndexChanged.connect(self.on_ignore_changed)
        self.ignmor.hide()
        self.igndir.setPlaceholderText('Exclude directory')
        self.igndir.setCompleter(self.completer)
        self.btn2 = QPushButton(QIcon.fromTheme("document-open"), ' Open ')
        self.btn2.clicked.connect(lambda: self.igndir.setText(str(
            QFileDialog.getExistingDirectory(self.dock,
            'Please, Open a Ignore Directory...', path.expanduser("~")))))
        vboxg1 = QVBoxLayout(self.group1)
        for each_widget in (QLabel('<b>Target directory path: '), self.outdir,
            self.btn1, self.btn1a, QLabel('<b>Ignore directory path: '),
            self.ignckb, self.ignmor, self.igndir, self.btn2, ):
            vboxg1.addWidget(each_widget)

        self.group2 = QGroupBox()
        self.group2.setTitle(' Output ')
        self.outfle = QLineEdit(path.join(path.expanduser("~"), 'output.html'))
        self.outfle.setPlaceholderText('Exclude directory')
        self.outfle.setCompleter(self.completer)
        self.btn3 = QPushButton(QIcon.fromTheme("document-save"), ' Save ')
        self.btn3.clicked.connect(lambda: self.outfle.setText(
            QFileDialog.getSaveFileName(self.dock, 'Save', path.expanduser("~"),
            'XML(*.xml)' if self.xmlo.isChecked() is True else 'HTML(*.html)')))
        vboxg2 = QVBoxLayout(self.group2)
        for each_widget in (QLabel('<b>Output report file path:'),
            self.outfle, self.btn3):
            vboxg2.addWidget(each_widget)

        self.group3 = QGroupBox()
        self.group3.setTitle(' Options ')
        self.group3.setCheckable(True)
        self.group3.setGraphicsEffect(QGraphicsBlurEffect(self))
        self.group3.graphicsEffect().setEnabled(False)
        self.group3.toggled.connect(self.toggle_options_group)
        self.qckb1, self.qckb2 = QCheckBox('Recursive'), QCheckBox('Time-less')
        self.qckb3, self.qckb4 = QCheckBox('Force Diff'), QCheckBox('Fast Mode')
        self.qckb5, self.tm = QCheckBox('Save a LOG file to target'), QLabel('')
        self.xmlo = QCheckBox('XML Output instead of HTML')
        self.opeo = QCheckBox('Open Clones Report when done')
        self.chrt = QCheckBox('LOW CPU priority for Backend Process')
        self.mdist, self.hdep, self.output = QSpinBox(), QSpinBox(), QTextEdit()
        self.ign_func = QLineEdit('test, forward, backward, Migration')
        self.mdist.setValue(5)
        self.hdep.setValue(1)
        self.mdist.setToolTip('''<b>Maximum amount of difference between pair of
        sequences in clone pair (5 default).Larger value more false positive''')
        self.hdep.setToolTip('''<b>Computation can be speeded up by increasing
                       this value, but some clones can be missed (1 default)''')
        [a.setChecked(True) for a in (self.qckb1, self.qckb3, self.qckb5,
                                      self.chrt, self.opeo)]
        vboxg3 = QVBoxLayout(self.group3)
        for each_widget in (self.qckb1, self.qckb2, self.qckb3, self.qckb4,
            self.qckb5, self.chrt, self.xmlo, self.opeo,
            QLabel('<b>Max Distance Threshold:'), self.mdist,
            QLabel('<b>Max Hashing Depth:'), self.hdep,
            QLabel('<b>Ignore code block prefix:'), self.ign_func):
            vboxg3.addWidget(each_widget)

        self.group4, self.auto = QGroupBox(), QComboBox()
        self.group4.setTitle(' Automation ')
        self.group4.setCheckable(True)
        self.group4.setToolTip('<font color="red"><b>WARNING:Advanced Setting!')
        self.group4.toggled.connect(lambda: self.group4.hide())
        self.auto.addItems(['Never run automatically', 'Run when File Saved',
            'Run when File Executed', 'Run when Tab Changed',
            'Run when File Opened', 'Run before File Saved'])
        self.auto.currentIndexChanged.connect(self.on_auto_changed)
        QVBoxLayout(self.group4).addWidget(self.auto)

        self.button = QPushButton(' Analyze for Clones ')
        self.button.setMinimumSize(75, 50)
        self.button.clicked.connect(self.run)
        glow = QGraphicsDropShadowEffect(self)
        glow.setOffset(0)
        glow.setBlurRadius(99)
        glow.setColor(QColor(99, 255, 255))
        self.button.setGraphicsEffect(glow)

        self.butkil = QPushButton(' Force Kill Clones ')
        self.butkil.clicked.connect(lambda: self.process.kill())

        class TransientWidget(QWidget):
            ' persistant widget thingy '
            def __init__(self, widget_list):
                ' init sub class '
                super(TransientWidget, self).__init__()
                vbox = QVBoxLayout(self)
                for each_widget in widget_list:
                    vbox.addWidget(each_widget)

        tw = TransientWidget((QLabel('<i>D.R.Y. principle analyzer'),
            self.group1, self.group2, self.group3, self.group4,
            QLabel('<b>Backend Logs'), self.output, self.tm, self.button,
            self.butkil))
        self.scrollable, self.dock = QScrollArea(), QDockWidget()
        self.scrollable.setWidgetResizable(True)
        self.scrollable.setWidget(tw)
        self.dock.setWindowTitle(__doc__)
        self.dock.setStyleSheet('QDockWidget::title{text-align: center;}')
        self.dock.setWidget(self.scrollable)
        ExplorerContainer().addTab(self.dock, "Clones")
        QPushButton(QIcon.fromTheme("help-about"), 'About', self.dock
            ).clicked.connect(lambda:
            QMessageBox.information(self.dock, __doc__, HELPMSG))

    def readOutput(self):
        """Read and append output to the logBrowser"""
        self.output.append(str(self.process.readAllStandardOutput()).strip())

    def readErrors(self):
        """Read and append errors to the logBrowser"""
        self.output.append(self.formatErrorMsg(str(
                                        self.process.readAllStandardError())))

    def formatErrorMsg(self, msg):
        """Format error messages in red color"""
        return self.formatMsg(msg, 'red')

    def formatInfoMsg(self, msg):
        """Format informative messages in blue color"""
        return self.formatMsg(msg, 'green')

    def formatMsg(self, msg, color):
        """Format message with the given color"""
        return '<font color="{}">{}</font>'.format(color, msg)

    def make_clon(self):
        ' make clones analyze from contextual sub menu '
        self.outdir.setText(
          self.locator.get_service('explorer').get_current_project_item().path)
        self.run()

    def run(self):
        ' run the actions '
        self.output.clear()
        self.output.append(self.formatInfoMsg('INFO:{}'.format(datetime.now())))
        self.tm.setText('<center><b>Last Clone: </b>' +
                        datetime.now().isoformat().split('.')[0])
        self.button.setDisabled(True)
        if not len(self.outdir.text()) and not len(self.outfle.text()):
            self.output.append(self.formatErrorMsg('ERROR: FAIL: Target empty'))
            self.button.setEnabled(True)
            return
        # run the subprocesses
        cmd = ' '.join((
            'chrt -i 0' if self.chrt.isChecked() is True else '', 'clonedigger',
            '' if self.qckb1.isChecked() is True else '--no-recursion',
            '--dont-print-time' if self.qckb2.isChecked() is True else '',
            '--force' if self.qckb3.isChecked() is True else '',
            '--fast' if self.qckb4.isChecked() is True else '',
            '--cpd-output' if self.xmlo.isChecked() is True else '',
            '' if self.xmlo.isChecked() is True else '--report-unifiers',
            '--distance-threshold={}'.format(self.mdist.value()),
            '--hashing-depth={}'.format(self.hdep.value()),
            '--ignore-dir="{}"'.format(self.igndir.text()
                if self.ignckb.currentIndex() is 0
                else self.ignmor.toPlainText()),
            '--func-prefixes="{}"'.format(self.ign_func.text()),
            '--output="{}"'.format(self.outfle.text()),
            '--language=python', path.abspath(self.outdir.text()),
        ))
        self.output.append(self.formatInfoMsg('INFO:OK:Command:{}'.format(cmd)))
        self.process.start(cmd)
        if not self.process.waitForStarted():
            self.output.append(self.formatErrorMsg(' ERROR: FAIL: Meh. '))
            self.output.append(self.formatErrorMsg('ERROR:FAIL:{}'.format(cmd)))
            self.button.setEnabled(True)
            return
        self.readOutput()
        self.readErrors()
        self.button.setEnabled(True)

    def _process_finished(self):
        """ finished sucessfully """
        self.output.append(self.formatInfoMsg('INFO:{}'.format(datetime.now())))
        # write a .log file on target
        if self.qckb5.isChecked() is True:
            log_file = 'ninja_clones.log'
            with open(path.join(str(self.outdir.text()), log_file), 'w') as log:
                self.output.append(self.formatInfoMsg('''INFO: OK: Writing Logs:
                    {}'''.format(path.join(str(self.outdir.text()), log_file))))
                log.write(self.output.toPlainText())
                log.close()
        # open target output
        if self.opeo.isChecked() is True and self.xmlo.isChecked() is False:
            try:
                startfile(self.outfle.text())
            except:
                Popen(["xdg-open", self.outfle.text()])
        self.output.selectAll()
        self.output.setFocus()

    def toggle_options_group(self):
        ' toggle on off the options group '
        if self.group3.isChecked() is True:
            [a.setChecked(True) for a in (self.qckb1, self.qckb3, self.qckb5,
                                          self.chrt, self.opeo)]
            self.mdist.setValue(5)
            self.hdep.setValue(1)
            self.group3.graphicsEffect().setEnabled(False)
        else:
            [a.setChecked(False) for a in (self.qckb1, self.qckb3, self.qckb5,
                                           self.chrt, self.opeo)]
            self.group3.graphicsEffect().setEnabled(True)

    def on_ignore_changed(self):
        'hide or show one widget or another depending what kind of input need'
        if self.ignckb.currentIndex() is 0:
            self.igndir.show()
            self.btn2.show()
            self.ignmor.hide()
        else:
            self.igndir.hide()
            self.btn2.hide()
            self.ignmor.show()

    def on_auto_changed(self):
        ' automation connects '
        if self.auto.currentIndex() is 1:
            self.locator.get_service('editor').fileSaved.connect(lambda:
                self.run())
            QMessageBox.information(self.dock, __doc__,
            '<b>Now Actions will Run Automatically when any File is Saved !')
        elif self.auto.currentIndex() is 2:
            self.locator.get_service('editor').fileExecuted.connect(lambda:
                self.run())
            QMessageBox.information(self.dock, __doc__,
            '<b>Now Actions will Run Automatically when any File is Executed !')
        elif self.auto.currentIndex() is 3:
            self.locator.get_service('editor').currentTabChanged.connect(lambda:
                self.run())
            QMessageBox.information(self.dock, __doc__,
            '<b>Now Actions will Run Automatically when current Tab is Changed')
        elif self.auto.currentIndex() is 4:
            self.locator.get_service('editor').fileOpened.connect(lambda:
                self.run())
            QMessageBox.information(self.dock, __doc__,
            '<b>Now Actions will Run Automatically when any File is Opened !')
        elif self.auto.currentIndex() is 5:
            self.locator.get_service('editor').beforeFileSaved.connect(lambda:
                self.run())
            QMessageBox.information(self.dock, __doc__,
            '<b>Now Actions will Run Automatically before any File is Saved !')
        self.group4.setDisabled(True)

    def finish(self):
        ' clear when finish '
        self.process.kill()