Fri, 28 Jun 2013 19:39:08 +0200
Completed the coding stuff of the QRegularExpression support.
--- a/Plugins/WizardPlugins/QRegularExpressionWizard/QRegularExpressionWizardDialog.py Thu Jun 27 19:51:57 2013 +0200 +++ b/Plugins/WizardPlugins/QRegularExpressionWizard/QRegularExpressionWizardDialog.py Fri Jun 28 19:39:08 2013 +0200 @@ -101,19 +101,18 @@ self.nextButton = None if fromEric: - self.buttonBox.button(QDialogButtonBox.Close).hide() + self.buttonBox.setStandardButtons( + QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.copyButton = None else: self.copyButton = \ self.buttonBox.addButton(self.trUtf8("Copy"), QDialogButtonBox.ActionRole) self.copyButton.setToolTip( self.trUtf8("Copy the regular expression to the clipboard")) - self.buttonBox.button(QDialogButtonBox.Ok).hide() - self.buttonBox.button(QDialogButtonBox.Cancel).hide() + self.buttonBox.setStandardButtons(QDialogButtonBox.Close) self.variableLabel.hide() self.variableLineEdit.hide() self.variableLine.hide() - self.importCheckBox.hide() self.regexpTextEdit.setFocus() def __sendCommand(self, command, **kw): @@ -514,119 +513,129 @@ # only available for Qt5 return - regex = self.regexpTextEdit.toPlainText() + regexp = self.regexpTextEdit.toPlainText() text = self.textTextEdit.toPlainText() - if regex and text: - options = QRegularExpression.NoPatternOption + if regexp and text: + options = [] if self.caseInsensitiveCheckBox.isChecked(): - options |= QRegularExpression.CaseInsensitiveOption + options.append("CaseInsensitiveOption") if self.multilineCheckBox.isChecked(): - options |= QRegularExpression.MultilineOption + options.append("MultilineOption") if self.dotallCheckBox.isChecked(): - options |= QRegularExpression.DotMatchesEverythingOption + options.append("DotMatchesEverythingOption") if self.extendedCheckBox.isChecked(): - options |= QRegularExpression.ExtendedPatternSyntaxOption + options.append("ExtendedPatternSyntaxOption") if self.greedinessCheckBox.isChecked(): - options |= QRegularExpression.InvertedGreedinessOption + options.append("InvertedGreedinessOption") if self.unicodeCheckBox.isChecked(): - options |= QRegularExpression.UseUnicodePropertiesOption + options.append("UseUnicodePropertiesOption") if self.captureCheckBox.isChecked(): - options |= QRegularExpression.DontCaptureOption - - re = QRegularExpression(regex, options) - if not re.isValid(): - E5MessageBox.critical(self, - self.trUtf8("Error"), - self.trUtf8("""Invalid regular expression: {0}""") - .format(re.errorString())) - # move cursor to error offset - offset = re.errorPatternOffset() - tc = self.regexpTextEdit.textCursor() - tc.setPosition(offset) - self.regexpTextEdit.setTextCursor(tc) - return - - match = re.match(text, startpos) - if match.hasMatch(): - captures = match.lastCapturedIndex() - else: - captures = 0 - row = 0 - OFFSET = 5 - - self.resultTable.setColumnCount(0) - self.resultTable.setColumnCount(3) - self.resultTable.setRowCount(0) - self.resultTable.setRowCount(OFFSET) - self.resultTable.setItem(row, 0, QTableWidgetItem(self.trUtf8("Regexp"))) - self.resultTable.setItem(row, 1, QTableWidgetItem(regex)) + options.append("DontCaptureOption") - if match.hasMatch(): - # index 0 is the complete match - offset = match.capturedStart(0) - self.lastMatchEnd = match.capturedEnd(0) - self.nextButton.setEnabled(True) - row += 1 - self.resultTable.setItem(row, 0, - QTableWidgetItem(self.trUtf8("Offset"))) - self.resultTable.setItem(row, 1, - QTableWidgetItem("{0:d}".format(match.capturedStart(0)))) - - row += 1 - self.resultTable.setItem(row, 0, - QTableWidgetItem(self.trUtf8("Captures"))) - self.resultTable.setItem(row, 1, - QTableWidgetItem("{0:d}".format(captures))) - row += 1 - self.resultTable.setItem(row, 1, - QTableWidgetItem(self.trUtf8("Text"))) - self.resultTable.setItem(row, 2, - QTableWidgetItem(self.trUtf8("Characters"))) - - row += 1 - self.resultTable.setItem(row, 0, - QTableWidgetItem(self.trUtf8("Match"))) - self.resultTable.setItem(row, 1, - QTableWidgetItem(match.captured(0))) - self.resultTable.setItem(row, 2, - QTableWidgetItem("{0:d}".format(match.capturedLength(0)))) - - for i in range(1, captures + 1): - if match.captured(i): - row += 1 - self.resultTable.insertRow(row) - self.resultTable.setItem(row, 0, - QTableWidgetItem(self.trUtf8("Capture #{0}").format(i))) - self.resultTable.setItem(row, 1, - QTableWidgetItem(match.captured(i))) - self.resultTable.setItem(row, 2, - QTableWidgetItem("{0:d}".format(match.capturedLength(i)))) - - # highlight the matched text - tc = self.textTextEdit.textCursor() - tc.setPosition(offset) - tc.setPosition(self.lastMatchEnd, QTextCursor.KeepAnchor) - self.textTextEdit.setTextCursor(tc) + if self.__sendCommand("execute", options=options, regexp=regexp, + text=text, startpos=startpos): + response = self.__receiveResponse() + if response and ("valid" in response or "matched" in response): + if "valid" in response: + E5MessageBox.critical(self, + self.trUtf8("Error"), + self.trUtf8("""Invalid regular expression: {0}""") + .format(response["errorMessage"])) + # move cursor to error offset + offset = response["errorOffset"] + tc = self.regexpTextEdit.textCursor() + tc.setPosition(offset) + self.regexpTextEdit.setTextCursor(tc) + self.regexpTextEdit.setFocus() + return + else: + row = 0 + OFFSET = 5 + + self.resultTable.setColumnCount(0) + self.resultTable.setColumnCount(3) + self.resultTable.setRowCount(0) + self.resultTable.setRowCount(OFFSET) + self.resultTable.setItem( + row, 0, QTableWidgetItem(self.trUtf8("Regexp"))) + self.resultTable.setItem( + row, 1, QTableWidgetItem(regexp)) + if response["matched"]: + captures = response["captures"] + # index 0 is the complete match + offset = captures[0][1] + self.lastMatchEnd = captures[0][2] + self.nextButton.setEnabled(True) + row += 1 + self.resultTable.setItem(row, 0, + QTableWidgetItem(self.trUtf8("Offset"))) + self.resultTable.setItem(row, 1, + QTableWidgetItem("{0:d}".format(offset))) + + row += 1 + self.resultTable.setItem(row, 0, + QTableWidgetItem(self.trUtf8("Captures"))) + self.resultTable.setItem(row, 1, + QTableWidgetItem("{0:d}".format(len(captures) - 1))) + row += 1 + self.resultTable.setItem(row, 1, + QTableWidgetItem(self.trUtf8("Text"))) + self.resultTable.setItem(row, 2, + QTableWidgetItem(self.trUtf8("Characters"))) + + row += 1 + self.resultTable.setItem(row, 0, + QTableWidgetItem(self.trUtf8("Match"))) + self.resultTable.setItem(row, 1, + QTableWidgetItem(captures[0][0])) + self.resultTable.setItem(row, 2, + QTableWidgetItem("{0:d}".format(captures[0][3]))) + + for i in range(1, len(captures)): + if captures[i][0]: + row += 1 + self.resultTable.insertRow(row) + self.resultTable.setItem(row, 0, + QTableWidgetItem( + self.trUtf8("Capture #{0}").format(i))) + self.resultTable.setItem(row, 1, + QTableWidgetItem(captures[i][0])) + self.resultTable.setItem(row, 2, + QTableWidgetItem("{0:d}".format(captures[i][3]))) + + # highlight the matched text + tc = self.textTextEdit.textCursor() + tc.setPosition(offset) + tc.setPosition(self.lastMatchEnd, QTextCursor.KeepAnchor) + self.textTextEdit.setTextCursor(tc) + else: + self.nextButton.setEnabled(False) + self.resultTable.setRowCount(2) + row += 1 + if startpos > 0: + self.resultTable.setItem(row, 0, + QTableWidgetItem(self.trUtf8("No more matches"))) + else: + self.resultTable.setItem(row, 0, + QTableWidgetItem(self.trUtf8("No matches"))) + + # remove the highlight + tc = self.textTextEdit.textCursor() + tc.setPosition(0) + self.textTextEdit.setTextCursor(tc) + + self.resultTable.resizeColumnsToContents() + self.resultTable.resizeRowsToContents() + self.resultTable.verticalHeader().hide() + self.resultTable.horizontalHeader().hide() + else: + E5MessageBox.critical(self, + self.trUtf8("Communication Error"), + self.trUtf8("""Invalid response received from PyQt5 backend.""")) else: - self.nextButton.setEnabled(False) - self.resultTable.setRowCount(2) - row += 1 - if startpos > 0: - self.resultTable.setItem(row, 0, - QTableWidgetItem(self.trUtf8("No more matches"))) - else: - self.resultTable.setItem(row, 0, - QTableWidgetItem(self.trUtf8("No matches"))) - - # remove the highlight - tc = self.textTextEdit.textCursor() - tc.setPosition(0) - self.textTextEdit.setTextCursor(tc) - - self.resultTable.resizeColumnsToContents() - self.resultTable.resizeRowsToContents() - self.resultTable.verticalHeader().hide() - self.resultTable.horizontalHeader().hide() + E5MessageBox.critical(self, + self.trUtf8("Communication Error"), + self.trUtf8("""Communication with PyQt5 backend failed.""")) else: E5MessageBox.critical(self, self.trUtf8("Error"),
--- a/Plugins/WizardPlugins/QRegularExpressionWizard/QRegularExpressionWizardServer.py Thu Jun 27 19:51:57 2013 +0200 +++ b/Plugins/WizardPlugins/QRegularExpressionWizard/QRegularExpressionWizardServer.py Fri Jun 28 19:39:08 2013 +0200 @@ -3,12 +3,13 @@ # Copyright (c) 2013 Detlev Offenbach <detlev@die-offenbachs.de> # +""" +Module implementing the PyQt5 server part of the QRegularExpression wizzard. +""" + import json import sys -def printerr(string): - sys.stderr.write(string) - sys.stderr.flush() def rxValidate(regexp, options): """ @@ -52,13 +53,66 @@ return valid, error, errorOffset -if __name__ == "__main__": +def rxExecute(regexp, options, text, startpos): + """ + Function to execute the given regular expression for a given text. + + @param regexp regular expression to validate (string) + @param options list of options (list of string) + @param text text to execute on (string) + @param startpos start position for the execution (integer) + @return tuple of a flag indicating a successful match (boolean) and + a list of captures containing the complete match as matched string + (string), match start (integer), match end (integer) and match length + (integer) for each entry + """ + valid, error, errorOffset = rxValidate(regexp, options) + if not valid: + return valid, error, errorOffset + + from PyQt5.QtCore import QRegularExpression + rxOptions = QRegularExpression.NoPatternOption + if "CaseInsensitiveOption" in options: + rxOptions |= QRegularExpression.CaseInsensitiveOption + if "MultilineOption" in options: + rxOptions |= QRegularExpression.MultilineOption + if "DotMatchesEverythingOption" in options: + rxOptions |= QRegularExpression.DotMatchesEverythingOption + if "ExtendedPatternSyntaxOption" in options: + rxOptions |= QRegularExpression.ExtendedPatternSyntaxOption + if "InvertedGreedinessOption" in options: + rxOptions |= QRegularExpression.InvertedGreedinessOption + if "UseUnicodePropertiesOption" in options: + rxOptions |= QRegularExpression.UseUnicodePropertiesOption + if "DontCaptureOption" in options: + rxOptions |= QRegularExpression.DontCaptureOption + + matched = False + captures = [] + re = QRegularExpression(regexp, rxOptions) + match = re.match(text, startpos) + if match.hasMatch(): + matched = True + for index in range(match.lastCapturedIndex() + 1): + captures.append([ + match.captured(index), + match.capturedStart(index), + match.capturedEnd(index), + match.capturedLength(index) + ]) + + return matched, captures + + +def main(): + """ + Function containing the main routine. + """ while True: commandStr = sys.stdin.readline() try: commandDict = json.loads(commandStr) responseDict = {"error": ""} - printerr(str(commandDict)) if "command" in commandDict: command = commandDict["command"] if command == "exit": @@ -75,6 +129,19 @@ responseDict["valid"] = valid responseDict["errorMessage"] = error responseDict["errorOffset"] = errorOffset + elif command == "execute": + valid, error, errorOffset = rxValidate(commandDict["regexp"], + commandDict["options"]) + if not valid: + responseDict["valid"] = valid + responseDict["errorMessage"] = error + responseDict["errorOffset"] = errorOffset + else: + matched, captures = rxExecute(commandDict["regexp"], + commandDict["options"], commandDict["text"], + commandDict["startpos"]) + responseDict["matched"] = matched + responseDict["captures"] = captures except ValueError as err: responseDict = {"error": str(err)} except Exception as err: @@ -84,3 +151,7 @@ sys.stdout.flush() sys.exit(0) + + +if __name__ == "__main__": + main()