コード例 #1
0
ファイル: highlighter.py プロジェクト: Morfyo/PYTHON
    def highlightBlock(self, text):
        """Apply syntax highlighting to the given block of text."""
        for expression, nth, format in self.rules:
            index = expression.indexIn(text, 0)

            while index >= 0:
                # We actually want the index of the nth match
                index = expression.pos(nth)
                length = expression.cap(nth).length()
                self.setFormat(index, length, format)
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(text, *self.tri_single)
            if not in_multiline:
                in_multiline = self.match_multiline(text, *self.tri_double)
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        #Spaces
        expression = QRegExp('\s+')
        index = expression.indexIn(text, 0)
        while index >= 0:
            index = expression.pos(0)
            length = expression.cap(0).length()
            self.setFormat(index, length, STYLES['spaces'])
            index = expression.indexIn(text, index + length)
コード例 #2
0
ファイル: highlighter.py プロジェクト: netantho/ninja-ide
    def run(self):
        self.msleep(300)
        block = self._highlighter.document().begin()
        while block.blockNumber() != -1:
            text = block.text()
            formats = []

            for expression, nth, char_format in self._highlighter.rules:
                index = expression.indexIn(text, 0)

                while index >= 0:
                    # We actually want the index of the nth match
                    index = expression.pos(nth)
                    length = expression.cap(nth).length()

                    formats.append((index, length, char_format))
                    index = expression.indexIn(text, index + length)

            # Spaces
            expression = QRegExp("\s+")
            index = expression.indexIn(text, 0)
            while index >= 0:
                index = expression.pos(0)
                length = expression.cap(0).length()
                formats.append((index, length, STYLES["spaces"]))
                index = expression.indexIn(text, index + length)

            self.styles[block.blockNumber()] = formats
            block = block.next()
コード例 #3
0
ファイル: owpythonscript.py プロジェクト: r0k3/orange3
    def highlightBlock(self, text):
        for pattern, format in self.rules:
            exp = QRegExp(pattern)
            index = exp.indexIn(text)
            while index >= 0:
                length = exp.matchedLength()
                if exp.numCaptures() > 0:
                    self.setFormat(exp.pos(1), len(str(exp.cap(1))), format)
                else:
                    self.setFormat(exp.pos(0), len(str(exp.cap(0))), format)
                index = exp.indexIn(text, index + length)

        # Multi line strings
        start = self.multilineStart
        end = self.multilineEnd

        self.setCurrentBlockState(0)
        startIndex, skip = 0, 0
        if self.previousBlockState() != 1:
            startIndex, skip = start.indexIn(text), 3
        while startIndex >= 0:
            endIndex = end.indexIn(text, startIndex + skip)
            if endIndex == -1:
                self.setCurrentBlockState(1)
                commentLen = len(text) - startIndex
            else:
                commentLen = endIndex - startIndex + 3
            self.setFormat(startIndex, commentLen, self.stringFormat)
            startIndex, skip = (start.indexIn(text,
                                              startIndex + commentLen + 3), 3)
コード例 #4
0
ファイル: pylint.py プロジェクト: khertan/KhtEditor
 def handle_stdout(self):
     """
     Private slot to handle the readyReadStdout
     signal of the pylint process.
     """
     result_list = []
     #regex = QRegExp('(\w)\S*:\S*(\d*):.*: (.*)')
     regex = QRegExp('(\w)\s*:\s*(\d*):(.*)')
     regex_score = \
         QRegExp('.*at.(\d.\d*)/10.*')
     while self.pylint_pross and self.pylint_pross.canReadLine():
         result = unicode(self.pylint_pross.readLine())
         if result != None:
             pos = 0
             while True:
                 pos = regex.indexIn(result, pos)
                 if pos < 0:
                     if regex_score.indexIn(result, 0) >= 0:
                         self.win.setWindowTitle( \
                             "PyLint Results :" \
                             + str(regex_score.cap(1)) \
                             + ':'                                
                             + os.path.basename(str(self.parent.editor.filename)))
                     break
                 result_list.append((regex.cap(1), regex.cap(2), regex.cap(3)))
                 #print 'Append : ',(regex.cap(1), regex.cap(2), regex.cap(3))
                 pos = pos + regex.matchedLength()
                 
     if len(result_list)>0:
         self.win.append_results(result_list)
コード例 #5
0
ファイル: pylint.py プロジェクト: tonal/KhtEditor
    def handle_stdout(self):
        """
        Private slot to handle the readyReadStdout
        signal of the pylint process.
        """
        result_list = []
        #regex = QRegExp('(\w)\S*:\S*(\d*):.*: (.*)')
        regex = QRegExp('(\w)\s*:\s*(\d*):(.*)')
        regex_score = \
            QRegExp('.*at.(\d.\d*)/10.*')
        while self.pylint_pross and self.pylint_pross.canReadLine():
            result = unicode(self.pylint_pross.readLine())
            if result != None:
                pos = 0
                while True:
                    pos = regex.indexIn(result, pos)
                    if pos < 0:
                        if regex_score.indexIn(result, 0) >= 0:
                            self.win.setWindowTitle( \
                                "PyLint Results :" \
                                + str(regex_score.cap(1)) \
                                + ':'
                                + os.path.basename(str(self.parent.editor.filename)))
                        break
                    result_list.append(
                        (regex.cap(1), regex.cap(2), regex.cap(3)))
                    #print 'Append : ',(regex.cap(1), regex.cap(2), regex.cap(3))
                    pos = pos + regex.matchedLength()

        if len(result_list) > 0:
            self.win.append_results(result_list)
コード例 #6
0
ファイル: BibleLoader.py プロジェクト: olivierkes/lexilogos
 def parse(self, text):
     """
     Parse some text, assuming it is in the form of:
         word 1414 {grammar} word 1414 {grammar} etc.
     and returns a list of tupples '(word, strong, grammar)'.
     """
     result = []
     r = QRegExp(r'([αβχδεφγηιϕκλμνοπθρστυςωξψζ]*)' +
                  '\s*' +
                  '([\d ]+)' +
                  '\s*' +
                  '\{(.*)\}\s*')
     r.setMinimal(True)
     pos = r.indexIn(text)
     while pos >= 0:
         # Some verbs have two strong numbers, we keep only the first,
         # the second one being grammar
         strong = r.cap(2).strip()
         if " " in strong:
             strong = strong.split(" ")[0]
         result.append((r.cap(1).strip(),
                        strong,
                        r.cap(3).strip()))
         pos = r.indexIn(text, pos + len(r.cap(0)))
     return result
コード例 #7
0
ファイル: highlighter.py プロジェクト: aguzubiaga/ninja-ide
    def run(self):
        styles = {}
        self.msleep(300)
        block = self._highlighter.document().begin()
        while block.blockNumber() != -1:
            text = block.text()
            formats = []

            for expression, nth, char_format in self._highlighter.rules:
                index = expression.indexIn(text, 0)

                while index >= 0:
                    # We actually want the index of the nth match
                    index = expression.pos(nth)
                    length = expression.cap(nth).length()

                    formats.append((index, length, char_format))
                    index = expression.indexIn(text, index + length)

            #Spaces
            expression = QRegExp('\s+')
            index = expression.indexIn(text, 0)
            while index >= 0:
                index = expression.pos(0)
                length = expression.cap(0).length()
                formats.append((index, length, STYLES['spaces']))
                index = expression.indexIn(text, index + length)

            styles[block.blockNumber()] = formats
            block = block.next()
        self.emit(SIGNAL("highlightingDetected(PyQt_PyObject)"), styles)
コード例 #8
0
ファイル: highlighter.py プロジェクト: jemandez/Traducciones
    def highlightBlock(self, text):
        """Apply syntax highlighting to the given block of text."""
        for expression, nth, format in self.rules:
            index = expression.indexIn(text, 0)

            while index >= 0:
                # We actually want the index of the nth match
                index = expression.pos(nth)
                length = expression.cap(nth).length()
                self.setFormat(index, length, format)
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(text, *self.tri_single)
            if not in_multiline:
                in_multiline = self.match_multiline(text, *self.tri_double)
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        #Spaces
        expression = QRegExp('\s+')
        index = expression.indexIn(text, 0)
        while index >= 0:
            index = expression.pos(0)
            length = expression.cap(0).length()
            self.setFormat(index, length, STYLES['spaces'])
            index = expression.indexIn(text, index + length)
コード例 #9
0
    def highlightBlock(self, text):
        #for every pattern
        for pattern, format in self.highlightingRules:
            #Create a regular expression from the retrieved pattern
            expression = QRegExp(pattern)
            #Check what index that expression occurs at with the ENTIRE text
            index = expression.indexIn(text)
            #While the index is greater than 0
            while index >= 0:
                #Get the length of how long the expression is true, set the format from the start to the length with the text format
                length = expression.matchedLength()
                self.setFormat(index, length, format)
                #Set index to where the expression ends in the text
                index = expression.indexIn(text, index + length)

        #HANDLE QUOTATION MARKS NOW.. WE WANT TO START WITH " AND END WITH ".. A THIRD " SHOULD NOT CAUSE THE WORDS INBETWEEN SECOND AND THIRD TO BE COLORED
        self.setCurrentBlockState(0)
        startIndex = 0
        if self.previousBlockState() != 1:
            startIndex = self.valueStartExpression.indexIn(text)
        while startIndex >= 0:
            endIndex = self.valueEndExpression.indexIn(text, startIndex)
            if endIndex == -1:
                self.setCurrentBlockState(1)
                commentLength = len(text) - startIndex
            else:
                commentLength = endIndex - startIndex + self.valueEndExpression.matchedLength(
                )
            self.setFormat(startIndex, commentLength, self.valueFormat)
            startIndex = self.valueStartExpression.indexIn(
                text, startIndex + commentLength)
コード例 #10
0
ファイル: highlighter.py プロジェクト: uKev/ninja-ide
    def run(self):
        self.msleep(300)
        block = self._highlighter.document().begin()
        while block.blockNumber() != -1:
            text = block.text()
            formats = []

            for expression, nth, char_format in self._highlighter.rules:
                index = expression.indexIn(text, 0)

                while index >= 0:
                    # We actually want the index of the nth match
                    index = expression.pos(nth)
                    length = expression.cap(nth).length()

                    formats.append((index, length, char_format))
                    index = expression.indexIn(text, index + length)

            #Spaces
            expression = QRegExp('\s+')
            index = expression.indexIn(text, 0)
            while index >= 0:
                index = expression.pos(0)
                length = expression.cap(0).length()
                formats.append((index, length, STYLES['spaces']))
                index = expression.indexIn(text, index + length)

            self.styles[block.blockNumber()] = formats
            block = block.next()
コード例 #11
0
ファイル: highlighter.py プロジェクト: daqing15/ninja-ide
    def run(self):
        """Execute this rules in another thread to avoid blocking the ui."""
        styles = {}
        self.msleep(300)
        block = self._highlighter.document().begin()
        while block.blockNumber() != -1:
            text = block.text()
            formats = []

            for expression, nth, char_format in self._highlighter.rules:
                index = expression.indexIn(text, 0)

                while index >= 0:
                    # We actually want the index of the nth match
                    index = expression.pos(nth)
                    length = len(expression.cap(nth))

                    formats.append((index, length, char_format))
                    index = expression.indexIn(text, index + length)

            # Spaces
            expression = QRegExp("\s+")
            index = expression.indexIn(text, 0)
            while index >= 0:
                index = expression.pos(0)
                length = len(expression.cap(0))
                formats.append((index, length, STYLES["spaces"]))
                index = expression.indexIn(text, index + length)

            styles[block.blockNumber()] = formats
            block = block.next()
        self.emit(SIGNAL("highlightingDetected(PyQt_PyObject)"), styles)
コード例 #12
0
    def highlightBlock(self, text):
        for pattern, format in self.rules:
            exp = QRegExp(pattern)
            index = exp.indexIn(text)
            while index >= 0:
                length = exp.matchedLength()
                if exp.numCaptures() > 0:
                    self.setFormat(exp.pos(1), len(str(exp.cap(1))), format)
                else:
                    self.setFormat(exp.pos(0), len(str(exp.cap(0))), format)
                index = exp.indexIn(text, index + length)

        # Multi line strings
        start = self.multilineStart
        end = self.multilineEnd

        self.setCurrentBlockState(0)
        startIndex, skip = 0, 0
        if self.previousBlockState() != 1:
            startIndex, skip = start.indexIn(text), 3
        while startIndex >= 0:
            endIndex = end.indexIn(text, startIndex + skip)
            if endIndex == -1:
                self.setCurrentBlockState(1)
                commentLen = len(text) - startIndex
            else:
                commentLen = endIndex - startIndex + 3
            self.setFormat(startIndex, commentLen, self.stringFormat)
            startIndex, skip = (start.indexIn(text,
                                              startIndex + commentLen + 3),
                                3)
コード例 #13
0
    def run(self):
        """Execute this rules in another thread to avoid blocking the ui."""
        styles = {}
        self.msleep(300)
        block = self._highlighter.document().begin()
        while block.blockNumber() != -1:
            text = block.text()
            formats = []

            for expression, nth, char_format in self._highlighter.rules:
                index = expression.indexIn(text, 0)

                while index >= 0:
                    # We actually want the index of the nth match
                    index = expression.pos(nth)
                    length = len(expression.cap(nth))

                    formats.append((index, length, char_format))
                    index = expression.indexIn(text, index + length)

            #Spaces
            expression = QRegExp('\s+')
            index = expression.indexIn(text, 0)
            while index >= 0:
                index = expression.pos(0)
                length = len(expression.cap(0))
                formats.append((index, length, STYLES['spaces']))
                index = expression.indexIn(text, index + length)

            styles[block.blockNumber()] = formats
            block = block.next()
        self.emit(SIGNAL("highlightingDetected(PyQt_PyObject)"), styles)
コード例 #14
0
class RegexHighlighter(QSyntaxHighlighter):
    def __init__(self, widget):
        QSyntaxHighlighter.__init__(self, widget)
        self.regex = None
        # create format type
        self.odd_format = QTextCharFormat()
        self.odd_format.setFontWeight(QFont.Bold)
        self.odd_format.setForeground(Qt.darkBlue)
        self.even_format = QTextCharFormat()
        self.even_format.setFontWeight(QFont.Bold)
        self.even_format.setForeground(Qt.darkMagenta)

    def set_regex(self, exp):
        self.regex = QRegExp(exp)

    def highlightBlock(self, text):
        if not self.regex:
            return
        matches = 0
        pos = self.regex.indexIn(text, 0)
        while (pos >= 0):
            length = self.regex.matchedLength()
            matches += 1
            if matches % 2 == 0:
                self.setFormat(pos, length, self.even_format)
            else:
                self.setFormat(pos, length, self.odd_format)
            # handle wildcard (*)
            if length == 0:
                pos += 1
            pos = self.regex.indexIn(text, pos + length)
コード例 #15
0
ファイル: lexer.py プロジェクト: andres53016/ejemplo1
	def highlightEntity(self,text,entity):
		if entity.style == self.Default:
			return
		expression=QRegExp(entity.pattern)
		index=expression.indexIn(text)
		while index>=0:
			length =expression.matchedLength()
			self.setFormat(index,length,self.style(entity.style))
			index=expression.indexIn(text,index+length)
コード例 #16
0
    def highlightBlock(self, text):
        for rule in self.highlightingRules:
            expression = QRegExp(rule.pattern)
            index = expression.indexIn(text)

            while index >= 0:
                length = expression.matchedLength()
                self.setFormat(index, length, rule.format)
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
コード例 #17
0
ファイル: highlighter.py プロジェクト: olivierdalang/stdm
    def highlightBlock(self, text):
        for rule in self.highlightingRules:
            expression = QRegExp(rule.pattern)
            index = expression.indexIn(text)

            while index >= 0:
                length = expression.matchedLength()
                self.setFormat(index, length, rule.format)
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
コード例 #18
0
ファイル: codeedit.py プロジェクト: andres53016/ejemplo1
    def getIndent(self,text):
		spaces= QRegExp("^(\\s*).*$")
		#~ indentation=""
		#~ if len(text) > 0 and text[-1] in [':', '{', '(', '[']:
				#~ indentation="	"
		if spaces.indexIn(text) == -1:
			return ""
		return spaces.cap(1)
コード例 #19
0
ファイル: browsers.py プロジェクト: karstenv/manageR
 def getData(self, error):
     if error:
         self.html = self.http.errorString()
     else:
         self.html = self.http.readAll()
     # the following is a hack to handle redirects...
     regexp = QRegExp(r'Redirect\sto\s\<a\shref=\"(.*)\">')
     if regexp.indexIn(QString(self.html)) > -1:
         self.setSource(QUrl(regexp.capturedTexts()[1]))
コード例 #20
0
ファイル: browsers.py プロジェクト: karstenv/manageR
 def find(self, name):
     location = QString(robjects.r.help(unicode(name))[0])
     regexp = QRegExp(r"(library.*)")
     start = regexp.indexIn(location)
     end = location.lastIndexOf("/")
     name = location[end+1:]
     location = location[start-1:]+".html"
     location.replace("help", "html")
     self.help_open(location, name, location)
コード例 #21
0
ファイル: lexer.py プロジェクト: andres53016/ejemplo1
	def highlightRegion(self,text,block):
		if block.style == self.Default:
			return
		startId=0
		self.setCurrentBlockState(0)
		start=QRegExp(block.start)
		end=QRegExp(block.end)
		if self.previousBlockState() != 1:
			startId= start.indexIn(text)
		while startId >= 0:
			endId=end.indexIn(text,startId)
			if endId == -1:
				self.setCurrentBlockState(1)
				length=text.length()-startId
			else:
				length=endId - startId +end.matchedLength()
			self.setFormat(startId,length,self.style(block.style))
			startId=start.indexIn(text,startId+length)
コード例 #22
0
ファイル: XMLHighlighter.py プロジェクト: Gustry/QuickOSM
    def highlightBlock(self, text):
        # for every pattern
        for pattern, char_format in self.highlightingRules:

            # Create a regular expression from the retrieved pattern
            expression = QRegExp(pattern)

            # Check what index that expression occurs at with the ENTIRE text
            index = expression.indexIn(text)

            # While the index is greater than 0
            while index >= 0:

                # Get the length of how long the expression is true,
                # set the format from the start to the length with
                # the text format
                length = expression.matchedLength()
                self.setFormat(index, length, char_format)

                # Set index to where the expression ends in the text
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)

        start_index = 0
        if self.previousBlockState() != 1:
            start_index = self.value_start_expression.indexIn(text)

        while start_index >= 0:
            end_index = self.value_end_expression.indexIn(text, start_index)

            if end_index == -1:
                self.setCurrentBlockState(1)
                comment_length = len(text) - start_index
            else:
                comment_length = \
                    end_index - start_index + \
                    self.value_end_expression.matchedLength()

            self.setFormat(start_index, comment_length, self.value_format)

            start_index = self.value_start_expression.indexIn(
                text, start_index + comment_length)
コード例 #23
0
    def highlightBlock(self, text):
        # for every pattern
        for pattern, char_format in self.highlightingRules:

            # Create a regular expression from the retrieved pattern
            expression = QRegExp(pattern)

            # Check what index that expression occurs at with the ENTIRE text
            index = expression.indexIn(text)

            # While the index is greater than 0
            while index >= 0:

                # Get the length of how long the expression is true,
                # set the format from the start to the length with
                # the text format
                length = expression.matchedLength()
                self.setFormat(index, length, char_format)

                # Set index to where the expression ends in the text
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)

        start_index = 0
        if self.previousBlockState() != 1:
            start_index = self.value_start_expression.indexIn(text)

        while start_index >= 0:
            end_index = self.value_end_expression.indexIn(text, start_index)

            if end_index == -1:
                self.setCurrentBlockState(1)
                comment_length = len(text) - start_index
            else:
                comment_length = \
                    end_index - start_index + \
                    self.value_end_expression.matchedLength()

            self.setFormat(start_index, comment_length, self.value_format)

            start_index = self.value_start_expression.indexIn(
                text, start_index + comment_length)
コード例 #24
0
ファイル: codeedit.py プロジェクト: andres53016/ejemplo1
    def findFoldableLines(self):
		#~ print "find"
		if self.lang == None:
			#~ print self.lang
			return
		#~ print self.lang
		#~ print "hola"
		#~ self.foldedLines.clear()
		self.foldableLines.clear()
		lines = self.toPlainText().split('\n')
		#~ print "hola",lines.count()
		for i in range(lines.count()):
			line = lines[i]
			for b in self.lang.blocks:
				sr = QRegExp(b.start)
				
				er = QRegExp(b.end)
				#~ print sr,er
				if sr.indexIn(line) != -1:
					#~ print sr.indexIn(line)
					start = i
					j = start
					encast = 0
					if er.indexIn(line) != -1:
						#~ print er.indexIn(line)
						continue
					while ( not (er.indexIn(line) != -1 and encast == 0) and (j < lines.count())):
						#~ print (not ((er.indexIn(line) != -1) and (encast == 0)) and (j < lines.count()))
						#~ print lines.count(),j
						
						line = lines[j]
						#~ print "hola"
						if sr.indexIn(line) !=-1:
							encast += 1
						if er.indexIn(line) != -1:
							encast -= 1
						j += 1
					self.foldableLines[start]=j-1
					#~ print self.foldableLines
		self.update()
コード例 #25
0
ファイル: highlighter.py プロジェクト: papablopo07/pireal
    def highlightBlock(self, text):
        """ Reimplementation """

        block_data = TextBlockData()
        # Paren
        index = self.paren.indexIn(text, 0)
        while index >= 0:
            matched_paren = str(self.paren.capturedTexts()[0])
            info = ParenInfo(matched_paren, index)
            block_data.insert_paren_info(info)
            index = self.paren.indexIn(text, index + 1)

        self.setCurrentBlockUserData(block_data)

        for pattern, _format in self._rules:
            expression = QRegExp(pattern)
            index = expression.indexIn(text)
            while index >= 0:
                length = expression.matchedLength()
                self.setFormat(index, length, _format)
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
コード例 #26
0
ファイル: data_model.py プロジェクト: Ariki/QGIS
        def getObject(self, row):
                val = self.data(self.index(row, 0), Qt.UserRole)
                fld = val if val is not None else self._getNewObject()
                fld.name = self.data(self.index(row, 0)) or ""

                typestr = self.data(self.index(row, 1)) or ""
                regex = QRegExp( "([^\(]+)\(([^\)]+)\)" )
                startpos = regex.indexIn( typestr )
                if startpos >= 0:
                        fld.dataType = regex.cap(1).strip()
                        fld.modifier = regex.cap(2).strip()
                else:
                        fld.modifier = None
                        fld.dataType = typestr

                fld.notNull = self.data(self.index(row, 2), Qt.CheckStateRole) == Qt.Unchecked
                fld.primaryKey = self.data(self.index(row, 1), Qt.UserRole)
                return fld
コード例 #27
0
    def __init__(self, row, table):
        TableField.__init__(self, table)
        self.num, self.name, self.dataType, self.charMaxLen, self.modifier, self.notNull, self.hasDefault, self.default, typeStr = row
        self.primaryKey = False

        # get modifier (e.g. "precision,scale") from formatted type string
        trimmedTypeStr = typeStr.strip()
        regex = QRegExp("\((.+)\)$")
        startpos = regex.indexIn(trimmedTypeStr)
        if startpos >= 0:
            self.modifier = regex.cap(1).strip()
        else:
            self.modifier = None

        # find out whether fields are part of primary key
        for con in self.table().constraints():
            if con.type == TableConstraint.TypePrimaryKey and self.num in con.columns:
                self.primaryKey = True
                break
コード例 #28
0
    def replaceAll(self):
        textEdit = self.textEdit

        count = 0

        if textEdit is not None and len(self.findComboBox.currentText()) > 0:
            findText = self.findComboBox.currentText()
            replaceText = self.replaceComboBox.currentText()
            caseSensitive = not self.ignoreCaseCheckBox.isChecked(
            ) and Qt.CaseSensitive or Qt.CaseInsensitive
            flagsIndex = self.flagsComboBox.currentIndex()
            if flagsIndex == self.STARTS_WITH:
                pattern = "\\b%s"
            elif flagsIndex == self.FULL_WORD:
                pattern = "\\b%s\\b"
            else:
                pattern = "%s"
            rx = QRegExp(pattern % findText)
            rx.setCaseSensitivity(caseSensitive)

            current = textEdit.toPlainText()
            pos = 0
            while pos >= 0:
                pos = rx.indexIn(current, pos)
                if pos >= 0:
                    pos += 1
                    count += 1
            replaced = current.replace(rx, replaceText)

            textEdit.selectAll()
            cursor = textEdit.textCursor()
            cursor.beginEditBlock()
            cursor.insertText(replaced)
            cursor.endEditBlock()

        if textEdit is not None:
            if not count:
                QApplication.beep()
                self.feedbackLabel.setText(self.tr("Not found"))
            else:
                self.feedbackLabel.setText(self.tr("%s replaced" % count))

            self.writeSettings()
コード例 #29
0
ファイル: connector.py プロジェクト: HeatherHillers/QGIS
    def getSpatialRefInfo(self, srid):
        if not self.has_spatial:
            return

        try:
            c = self._execute(None, "SELECT srtext FROM spatial_ref_sys WHERE srid = '%d'" % srid)
        except DbError as e:
            return
        sr = self._fetchone(c)
        self._close_cursor(c)
        if sr is None:
            return

        srtext = sr[0]
        # try to extract just SR name (should be quoted in double quotes)
        regex = QRegExp('"([^"]+)"')
        if regex.indexIn(srtext) > -1:
            srtext = regex.cap(1)
        return srtext
コード例 #30
0
    def getSpatialRefInfo(self, srid):
        if not self.has_spatial:
            return

        try:
            c = self._execute(None, "SELECT srtext FROM spatial_ref_sys WHERE srid = '%d'" % srid)
        except DbError as e:
            return
        sr = self._fetchone(c)
        self._close_cursor(c)
        if sr is None:
            return

        srtext = sr[0]
        # try to extract just SR name (should be quoted in double quotes)
        regex = QRegExp('"([^"]+)"')
        if regex.indexIn(srtext) > -1:
            srtext = regex.cap(1)
        return srtext
コード例 #31
0
ファイル: plugin.py プロジェクト: TranDinhTrung/QGIS
    def __init__(self, row, table):
        TableField.__init__(self, table)
        self.num, self.name, self.dataType, self.charMaxLen, self.modifier, self.notNull, self.hasDefault, self.default, typeStr = row
        self.primaryKey = False

        # get modifier (e.g. "precision,scale") from formatted type string
        trimmedTypeStr = typeStr.strip()
        regex = QRegExp("\((.+)\)$")
        startpos = regex.indexIn(trimmedTypeStr)
        if startpos >= 0:
            self.modifier = regex.cap(1).strip()
        else:
            self.modifier = None

        # find out whether fields are part of primary key
        for con in self.table().constraints():
            if con.type == TableConstraint.TypePrimaryKey and self.num in con.columns:
                self.primaryKey = True
                break
コード例 #32
0
ファイル: data_model.py プロジェクト: wsyscu/QGIS
    def getObject(self, row):
        val = self.data(self.index(row, 0), Qt.UserRole)
        fld = val if val is not None else self._getNewObject()
        fld.name = self.data(self.index(row, 0)) or ""

        typestr = self.data(self.index(row, 1)) or ""
        regex = QRegExp("([^\(]+)\(([^\)]+)\)")
        startpos = regex.indexIn(typestr)
        if startpos >= 0:
            fld.dataType = regex.cap(1).strip()
            fld.modifier = regex.cap(2).strip()
        else:
            fld.modifier = None
            fld.dataType = typestr

        fld.notNull = self.data(self.index(row, 2),
                                Qt.CheckStateRole) == Qt.Unchecked
        fld.primaryKey = self.data(self.index(row, 1), Qt.UserRole)
        return fld
コード例 #33
0
    def replaceAll(self):
        textEdit = self.textEdit
        
        count = 0
        
        if textEdit is not None and len(self.findComboBox.currentText()) > 0:
            findText = self.findComboBox.currentText()
            replaceText = self.replaceComboBox.currentText()
            caseSensitive = not self.ignoreCaseCheckBox.isChecked() and Qt.CaseSensitive or Qt.CaseInsensitive
            flagsIndex = self.flagsComboBox.currentIndex()
            if flagsIndex == self.STARTS_WITH:
                pattern = "\\b%s"
            elif flagsIndex == self.FULL_WORD:
                pattern = "\\b%s\\b"
            else:
                pattern = "%s"
            rx = QRegExp(pattern % findText)
            rx.setCaseSensitivity(caseSensitive)

            current = textEdit.toPlainText()
            pos = 0
            while pos >= 0:
                pos = rx.indexIn(current, pos)
                if pos >= 0:
                    pos += 1
                    count += 1 
            replaced = current.replace(rx, replaceText)

            textEdit.selectAll()
            cursor = textEdit.textCursor()
            cursor.beginEditBlock()
            cursor.insertText(replaced)
            cursor.endEditBlock()

        if textEdit is not None:
            if not count:
                QApplication.beep()
                self.feedbackLabel.setText(self.tr("Not found"))
            else:
                self.feedbackLabel.setText(self.tr("%s replaced" % count))
                
            self.writeSettings()
コード例 #34
0
ファイル: groups_list.py プロジェクト: maximerobin/Ufwi
    def applyFilter(self, filterby, filter):
        self.filter = filter
        self.filter_by = filterby
        if filter == u'' or filterby == '':
            self.filtered_edw_list = copy(self.edw_list)
        else:
            self.filtered_edw_list = []
            rx = QRegExp(filter)
            rx.setCaseSensitivity(Qt.CaseInsensitive)

            for edw in self.edw_list:
                grp_name = unicode(edw.getVal(filterby))
                if rx.indexIn(grp_name) != -1:
                    self.filtered_edw_list.append(edw)
        self.refreshGroups()

        # refresh the cell display
        for table in self.grouped_tables.itervalues():
            table.refresh(None)
            table.disable_display = False
            table.refreshDisplay()
コード例 #35
0
ファイル: BibleLoader.py プロジェクト: olivierkes/lexilogos
    def parseBook(self, book):
        """
        Takes a raw book and returns an array of array of array such as:
        [0][0][0] = Mt 1, 1
        [1][8][4] = Mr 9, 5
        i.e. [bookNumber][chapter][verse]
        Note that chapter and verse start from 0.
        etc.
        """
        result = []
        line = book.split("\n")

        # Puts each verse on one line
        verse = []
        v = ""
        for l in line:
            if l[0:5] == "     ":
                if v: verse.append(v)
                v = l[5:]
            else:
                v += " " + l
        if v: verse.append(v)

        r = QRegExp(r'(\d+):(\d+)\s*(.*)')
        chapter = []
        for v in verse:
            pos = r.indexIn(v)
            chap = int(r.cap(1))
            ver = int(r.cap(2))
            text = r.cap(3).strip()
            # Variantes textuelles
            text = re.sub(r'\|( .* )\|( .* )\|', r'\1', text)
            if ver == 1:
                if len(chapter) > 0: result.append(chapter)
                chapter = [text]
            else:
                chapter.append(text)
        if len(chapter) > 0: result.append(chapter)

        return result
コード例 #36
0
 def textUnderCursor(self):
     """
     Return current word at cursor position
     """
     line, index = self.getCursorPosition()
     text = self.text(line)
     wc = self.wordCharacters()
     if wc is None:
         regexp = QRegExp('[^\w_]')
     else:
         regexp = QRegExp('[^{0}]'.format(re.escape(wc)))
     start = regexp.lastIndexIn(text, index) + 1
     end = regexp.indexIn(text, index)
     if start == end + 1 and index > 0:
         # we are on a word boundary, try again
         start = regexp.lastIndexIn(text, index - 1) + 1
     if start == -1: start = 0
     if end == -1: end = len(text)
     if end > start:
         word = text[start:end]
     else:
         word = ''
     return word
コード例 #37
0
ファイル: pqEdit.py プロジェクト: jlg234bob/PPQT
class PPTextEditor(QPlainTextEdit):
    # Initialize the editor on creation.
    def __init__(self, parent=None, fontsize=12):
        super(PPTextEditor, self).__init__(parent)
        # Do not allow line-wrap; horizontal scrollbar appears when required.
        self.setLineWrapMode(QPlainTextEdit.NoWrap)
        # make sure when we jump to a line, it goes to the window center
        self.setCenterOnScroll(True)
        # Get a monospaced font as selected by the user with View>Font
        self.setFont(pqMsgs.getMonoFont(fontsize, True))
        # instantiate our "syntax" highlighter object, but link it to an empty
        # QTextDocument. We will redirect it to our actual document only after
        # loading a document, as it relies on metadata, and then only when/if
        # the IMC.*HiliteSwitch es are on.
        self.nulDoc = QTextDocument()  # make a null document
        self.hiliter = wordHighLighter(self.nulDoc)
        # all the metadata lists will be initialized when self.clear() is
        # called from pqMain, shortly.
        # save a regex for quickly finding if a selection is a single word
        self.oneWordRE = QRegExp(u'^\W*(\w{2,})\W*$')
        self.menuWord = QString()
        # Create and initialize an SHA-1 hash machine
        self.cuisineart = QCryptographicHash(QCryptographicHash.Sha1)

    # switch on or off our text-highlighting. By switching the highlighter
    # to a null document we remove highlighting; by switching it back to
    # the real document, we cause re-highlighting of everything. This makes
    # significant delay for a large document, so put up a status message
    # during it by starting and ending a progress bar.
    def setHighlight(self, onoff):
        self.hiliter.setDocument(self.nulDoc)  # turn off hiliting always
        if onoff:
            pqMsgs.showStatusMsg("Setting Scanno/Spelling Highlights...")
            self.hiliter.setDocument(self.document())
            pqMsgs.clearStatusMsg()

    # Implement clear/new. Just toss everything we keep.
    def clear(self):
        self.setHighlight(False)
        self.document().clear()
        self.document().setModified(False)
        self.bookMarkList = \
            [None, None, None, None, None, None, None, None, None]
        IMC.pageTable.clear()
        IMC.goodWordList.clear()
        IMC.badWordList.clear()
        IMC.wordCensus.clear()
        IMC.charCensus.clear()
        IMC.notesEditor.clear()
        IMC.pngPanel.clear()
        IMC.needSpellCheck = False
        IMC.needMetadataSave = 0x00
        IMC.staleCensus = 0x00
        IMC.bookSaveEncoding = QString(u'UTF-8')
        IMC.bookMainDict = IMC.spellCheck.mainTag
        # force a cursor "move" in order to create a cursorMoved signal that will
        # clear the status line - then undo it so the document isn't modified.
        self.textCursor().insertText(QString(' '))
        self.document().undo()

    # Implement the Edit menu items:
    # Edit > ToUpper,  Edit > ToTitle,  Edit > ToLower
    # Note that in full Unicode, changing letter case is not so simple as it
    # was in Latin-1! We use the QChar and QString facilities to do it, and
    # a regex in a loop to pick off words. Restore the current selection after
    # so another operation can be done on it.
    # N.B. it is not possible to do self.textCursor().setPosition(), it seems
    # that self.textCursor() is "const". One has to create a new cursor,
    # position it, and install it on the document with self.setTextCursor().
    def toUpperCase(self):
        global reWord
        tc = QTextCursor(self.textCursor())
        if not tc.hasSelection():
            return  # no selection, nothing to do
        startpos = tc.selectionStart()
        endpos = tc.selectionEnd()
        qs = QString(tc.selectedText())  # copy of selected text
        i = reWord.indexIn(qs, 0)  # index of first word if any
        if i < 0: return  # no words in selection, exit
        while i >= 0:
            w = reWord.cap(0)  # found word as QString
            n = w.size()  # its length
            qs.replace(i, n, w.toUpper())  # replace it with UC version
            i = reWord.indexIn(qs, i + n)  # find next word if any
        # we have changed at least one word, replace selection with altered text
        tc.insertText(qs)
        # that wiped the selection, so restore it by "dragging" left to right
        tc.setPosition(startpos, QTextCursor.MoveAnchor)  # click
        tc.setPosition(endpos, QTextCursor.KeepAnchor)  # drag
        self.setTextCursor(tc)

    # to-lower is identical except for the method call.
    def toLowerCase(self):
        global reWord  # the regex \b\w+\b
        tc = QTextCursor(self.textCursor())
        if not tc.hasSelection():
            return  # no selection, nothing to do
        startpos = tc.selectionStart()
        endpos = tc.selectionEnd()
        qs = QString(tc.selectedText())  # copy of selected text
        i = reWord.indexIn(qs, 0)  # index of first word if any
        if i < 0: return  # no words in selection, exit
        while i >= 0:
            w = reWord.cap(0)  # found word as QString
            n = w.size()  # its length
            qs.replace(i, n, w.toLower())  # replace it with UC version
            i = reWord.indexIn(qs, i + n)  # find next word if any
        # we have changed at least one word, replace selection with altered text
        tc.insertText(qs)
        # that wiped the selection, so restore it by "dragging" left to right
        tc.setPosition(startpos, QTextCursor.MoveAnchor)  # click
        tc.setPosition(endpos, QTextCursor.KeepAnchor)  # drag
        self.setTextCursor(tc)

    # toTitle is similar but we have to change the word to lowercase (in case
    # it is uppercase now) and then change the initial character to upper.
    # Note it would be possible to write a smarter version that looked up the
    # word in a list of common adjectives, connectives, and adverbs and avoided
    # capitalizing a, and, of, by and so forth. Not gonna happen.
    def toTitleCase(self):
        global reWord  # the regex \b\w+\b
        self.toLowerCase()
        tc = QTextCursor(self.textCursor())
        if not tc.hasSelection():
            return  # no selection, nothing to do
        startpos = tc.selectionStart()
        endpos = tc.selectionEnd()
        qs = QString(tc.selectedText())  # copy of selected text
        i = reWord.indexIn(qs, 0)  # index of first word if any
        if i < 0: return  # no words in selection, exit
        while i >= 0:
            w = reWord.cap(0)  # found word as QString
            n = w.size()
            qs.replace(i, 1, qs.at(i).toUpper())  # replace initial with UC
            i = reWord.indexIn(qs, i + n)  # find next word if any
        # we have changed at least one word, replace selection with altered text
        tc.insertText(qs)
        # that wiped the selection, so restore it by "dragging" left to right
        tc.setPosition(startpos, QTextCursor.MoveAnchor)  # click
        tc.setPosition(endpos, QTextCursor.KeepAnchor)  # drag
        self.setTextCursor(tc)

    # Re-implement the parent's keyPressEvent in order to provide some
    # special controls. (Note on Mac, "ctrl-" is "cmd-" and "alt-" is "opt-")
    # ctrl-plus increases the edit font size 1 pt
    # (n.b. ctrl-plus probably only comes from a keypad, we usually just get
    #  ctrl-shift-equals instead of plus)
    # ctrl-minus decreases the edit font size 1 pt
    # ctrl-<n> for n in 1..9 jumps the insertion point to bookmark <n>
    # ctrl-shift-<n> extends the selection to bookmark <n>
    # ctrl-alt-<n> sets bookmark n at the current position
    def keyPressEvent(self, event):
        #pqMsgs.printKeyEvent(event)
        kkey = int(int(event.modifiers()) & IMC.keypadDeModifier) | int(
            event.key())
        # add as little overhead as possible: if it isn't ours, pass it on.
        if kkey in IMC.keysOfInterest:  # we trust python to do this quickly
            event.accept()  # we handle this one
            if kkey in IMC.findKeys:
                # ^f, ^g, etc. -- just pass them straight to the Find panel
                self.emit(SIGNAL("editKeyPress"), kkey)
            elif kkey in IMC.zoomKeys:
                # n.b. the self.font and setFont methods inherit from QWidget
                # Point increment by which to change.
                n = (-1) if (kkey == IMC.ctl_minus) else 1
                # Actual point size currently in use, plus increment
                p = self.fontInfo().pointSize() + n
                if (p > 3) and (p < 65):  # don't let's get ridiculous, hmm?
                    # Simply calling self.font().setPointSize() had no effect,
                    # we have to actually call setFont() to make change happen.
                    f = self.font()  # so get our font,
                    f.setPointSize(p)  # change its point size +/-
                    self.setFont(f)  # and put the font back
                    IMC.fontSize = p  # and remember the size for shutdown time
            elif kkey in IMC.markKeys:  # ^1-9, jump to bookmark
                bkn = kkey - IMC.ctl_1  # make it 0-8
                if self.bookMarkList[
                        bkn] is not None:  # if that bookmark is set,
                    self.setTextCursor(self.bookMarkList[bkn])  # jump to it
            elif kkey in IMC.markShiftKeys:  # shift-ctl-1/9, select to mark
                # Make our document cursor's selection go from our current ANCHOR
                # to the POSITION from the bookmark cursor.
                mark_tc = self.bookMarkList[kkey - IMC.ctl_shft_1]
                if mark_tc is not None:
                    tc = QTextCursor(self.textCursor())
                    tc.setPosition(mark_tc.position(), QTextCursor.KeepAnchor)
                    self.setTextCursor(tc)
            elif kkey in IMC.markSetKeys:  # ctl-alt-1-9, set a bookmark
                bkn = kkey - IMC.ctl_alt_1  # make it 0-8
                self.bookMarkList[bkn] = QTextCursor(self.textCursor())
                IMC.needMetadataSave |= IMC.bookmarksChanged
        else:  # not in keysOfInterest, so pass it up to parent
            event.ignore()
            super(PPTextEditor, self).keyPressEvent(event)

    # Called from pqFind after doing a successful search, this method centers the
    # current selection (which is the result of the find) in the window. If the selection
    # is large, put the top of the selection higher than center but on no account
    # above the top of the viewport. Two problems arise: One, the rectangles returned
    # by .cursorRect() and by .viewport().geometry() are in pixel units, while the
    # vertical scrollbar is sized in logical text lines. So we work out the adjustment
    # as a fraction of the viewport, times the scrollbar's pageStep value to get lines.
    # Two, cursorRect gives only the height of the actual cursor, not of the selected
    # text. To find out the height of the full selection we have to get a cursorRect
    # for the start of the selection, and another for the end of it.
    def centerCursor(self):
        tc = QTextCursor(
            self.textCursor())  # copy the working cursor with its selection
        top_point = tc.position()  # one end of selection, in character units
        bot_point = tc.anchor()  # ..and the other end
        if top_point > bot_point:  # often the position is > the anchor
            (top_point, bot_point) = (bot_point, top_point)
        tc.setPosition(top_point)  # cursor for the top of the selection
        selection_top = self.cursorRect(tc).top()  # ..get its top pixel
        line_height = self.cursorRect(
            tc).height()  # and save height of one line
        tc.setPosition(bot_point)  # cursor for the end of the selection
        selection_bot = self.cursorRect(
            tc).bottom()  # ..selection's bottom pixel
        selection_height = selection_bot - selection_top + 1  # selection height in pixels
        view_height = self.viewport().geometry().height(
        )  # scrolled area's height in px
        view_half = view_height >> 1  # int(view_height/2)
        pixel_adjustment = 0
        if selection_height < view_half:
            # selected text is less than half the window height: center the top of the
            # selection, i.e., make the cursor_top equal to view_half.
            pixel_adjustment = selection_top - view_half  # may be negative
        else:
            # selected text is taller than half the window, can we show it all?
            if selection_height < (view_height - line_height):
                # all selected text fits in the viewport (with a little free): center it.
                pixel_adjustment = (selection_top +
                                    (selection_height / 2)) - view_half
            else:
                # not all selected text fits the window, put text top near window top
                pixel_adjustment = selection_top - line_height
        # OK, convert the pixel adjustment to a line-adjustment based on the assumption
        # that a scrollbar pageStep is the height of the viewport in lines.
        adjust_fraction = pixel_adjustment / view_height
        vscroller = self.verticalScrollBar()
        page_step = vscroller.pageStep(
        )  # lines in a viewport page, actually less 1
        adjust_lines = int(page_step * adjust_fraction)
        target = vscroller.value() + adjust_lines
        if (target >= 0) and (target <= vscroller.maximum()):
            vscroller.setValue(target)

    # Catch the contextMenu event and extend the standard context menu with
    # a separator and the option to add a word to good-words, but only when
    # there is a selection and it encompasses just one word.
    def contextMenuEvent(self, event):
        ctx_menu = self.createStandardContextMenu()
        if self.textCursor().hasSelection:
            qs = self.textCursor().selectedText()
            if 0 == self.oneWordRE.indexIn(
                    qs):  # it matches at 0 or not at all
                self.menuWord = self.oneWordRE.cap(1)  # save the word
                ctx_menu.addSeparator()
                gw_name = QString(self.menuWord)  # make a copy
                gw_action = ctx_menu.addAction(
                    gw_name.append(QString(u' -> Goodwords')))
                self.connect(gw_action, SIGNAL("triggered()"), self.addToGW)
        ctx_menu.exec_(event.globalPos())

    # This slot receives the "someword -> good_words" context menu action
    def addToGW(self):
        IMC.goodWordList.insert(self.menuWord)
        IMC.needMetadataSave |= IMC.goodwordsChanged
        IMC.needSpellCheck = True
        IMC.mainWindow.setWinModStatus()

    # Implement save: the main window opens the files for output using
    # QIODevice::WriteOnly, which wipes the contents (contrary to the doc)
    # so we need to write the document and metadata regardless of whether
    # they've been modified. However we avoid rebuilding metadata if we can.
    def save(self, dataStream, metaStream):
        # Get the contents of the document as a QString
        doc_text = self.toPlainText()
        # Calculate the SHA-1 hash over the document and save it in both hash
        # fields of the IMC.
        self.cuisineart.reset()
        self.cuisineart.addData(doc_text)
        IMC.metaHash = IMC.documentHash = bytes(
            self.cuisineart.result()).__repr__()
        # write the document, which is pretty simple in the QStream world
        dataStream << doc_text
        dataStream.flush()
        #self.rebuildMetadata() # update any census that needs it
        self.writeMetadata(metaStream)
        metaStream.flush()
        IMC.needMetadataSave = 0x00
        self.document().setModified(
            False)  # this triggers main.setWinModStatus()

    def writeMetadata(self, metaStream):
        # Writing the metadata takes a bit more work.
        # pageTable goes out between {{PAGETABLE}}..{{/PAGETABLE}}
        metaStream << u"{{VERSION 0}}\n"  # meaningless at the moment
        metaStream << u"{{ENCODING "
        metaStream << unicode(IMC.bookSaveEncoding)
        metaStream << u"}}\n"
        metaStream << u"{{STALECENSUS "
        if 0 == IMC.staleCensus:
            metaStream << u"FALSE"
        else:
            metaStream << u"TRUE"
        metaStream << u"}}\n"
        metaStream << u"{{NEEDSPELLCHECK "
        if 0 == IMC.needSpellCheck:
            metaStream << u"FALSE"
        else:
            metaStream << u"TRUE"
        metaStream << u"}}\n"
        metaStream << u"{{MAINDICT "
        metaStream << unicode(IMC.bookMainDict)
        metaStream << u"}}\n"
        # The hash could contain any character. Using __repr__ ensured
        # it is enclosed in balanced single or double quotes but to be
        # double sure we will fence it in characters we can spot with a regex.
        metaStream << u"{{DOCHASH " + IMC.documentHash + u" }}\n"
        if IMC.pageTable.size():
            metaStream << u"{{PAGETABLE}}\n"
            for i in range(IMC.pageTable.size()):
                metaStream << IMC.pageTable.metaStringOut(i)
            metaStream << u"{{/PAGETABLE}}\n"
        if IMC.charCensus.size():
            metaStream << u"{{CHARCENSUS}}\n"
            for i in range(IMC.charCensus.size()):
                (w, n, f) = IMC.charCensus.get(i)
                metaStream << "{0} {1} {2}\n".format(unicode(w), n, f)
            metaStream << u"{{/CHARCENSUS}}\n"
        if IMC.wordCensus.size():
            metaStream << u"{{WORDCENSUS}}\n"
            for i in range(IMC.wordCensus.size()):
                (w, n, f) = IMC.wordCensus.get(i)
                metaStream << "{0} {1} {2}\n".format(unicode(w), n, f)
            metaStream << u"{{/WORDCENSUS}}\n"
        metaStream << u"{{BOOKMARKS}}\n"
        for i in range(9):  # 0..8
            if self.bookMarkList[i] is not None:
                metaStream << "{0} {1} {2}\n".format(
                    i, self.bookMarkList[i].position(),
                    self.bookMarkList[i].anchor())
        metaStream << u"{{/BOOKMARKS}}\n"
        metaStream << u"{{NOTES}}\n"
        d = IMC.notesEditor.document()
        if not d.isEmpty():
            for i in range(d.blockCount()):
                t = d.findBlockByNumber(i).text()
                if t.startsWith("{{"):
                    t.prepend(u"\xfffd")  # Unicode Replacement char
                metaStream << t + "\n"
            IMC.notesEditor.document().setModified(False)
        metaStream << u"{{/NOTES}}\n"
        if IMC.goodWordList.active():  # have some good words
            metaStream << u"{{GOODWORDS}}\n"
            IMC.goodWordList.save(metaStream)
            metaStream << u"{{/GOODWORDS}}\n"
        if IMC.badWordList.active():  # have some bad words
            metaStream << u"{{BADWORDS}}\n"
            IMC.badWordList.save(metaStream)
            metaStream << u"{{/BADWORDS}}\n"
        p1 = self.textCursor().selectionStart()
        p2 = self.textCursor().selectionEnd()
        metaStream << u"{{CURSOR " + unicode(p1) + u' ' + unicode(p2) + u"}}\n"
        metaStream.flush()

    # Implement load: the main window has the job of finding and opening files
    # then passes QTextStreams ready to read here. If metaStream is None,
    # no metadata file was found and we construct the metadata.
    # n.b. before main calls here, it calls our .clear, hence lists are
    # empty, hiliting is off, etc.

    def load(self, dataStream, metaStream, goodStream, badStream):
        # Load the document file into the editor
        self.setPlainText(dataStream.readAll())
        # Initialize the hash value for the document, which will be equal unless
        # we read something different from the metadata file.
        self.cuisineart.reset()
        self.cuisineart.addData(self.toPlainText())
        IMC.metaHash = IMC.documentHash = bytes(
            self.cuisineart.result()).__repr__()
        if metaStream is None:
            # load goodwords, badwords, and take census
            if goodStream is not None:
                IMC.goodWordList.load(goodStream)
            if badStream is not None:
                IMC.badWordList.load(badStream)
            self.rebuildMetadata(
                page=True)  # build page table & vocab from scratch
        else:
            self.loadMetadata(metaStream)
        # If the metaData and document hashes now disagree, it is because the metadata
        # had a DOCHASH value for a different book or version. Warn the user.
        if IMC.metaHash != IMC.documentHash:
            pqMsgs.warningMsg(
                u"The document file and metadata file do not match!",
                u"Bookmarks, page breaks and other metadata will be wrong! Strongly recommend you not edit or save this book."
            )
        # restore hiliting if the user wanted it. Note this can cause a
        # serious delay if the new book is large. However the alternative is
        # to not set it on and then we are out of step with the View menu
        # toggles, so the user has to set it off before loading, or suffer.
        self.setHighlight(IMC.scannoHiliteSwitch or IMC.spellingHiliteSwitch)
        # set a different main dict if there was one in the metadata
        if IMC.bookMainDict is not None:
            IMC.spellCheck.setMainDict(IMC.bookMainDict)

    # load page table & vocab from the .meta file as a stream.
    # n.b. QString has a split method we could use but instead
    # we take the input line to a Python u-string and split it. For
    # the word/char census we have to take the key back to a QString.
    def loadMetadata(self, metaStream):
        sectionRE = QRegExp( u"\{\{(" + '|'.join (
            ['PAGETABLE','CHARCENSUS','WORDCENSUS','BOOKMARKS',
             'NOTES','GOODWORDS','BADWORDS','CURSOR','VERSION',
             'STALECENSUS','NEEDSPELLCHECK','ENCODING', 'DOCHASH', 'MAINDICT'] ) \
                             + u")(.*)\}\}",
            Qt.CaseSensitive)
        metaVersion = 0  # base version
        while not metaStream.atEnd():
            qline = metaStream.readLine().trimmed()
            if qline.isEmpty(): continue  # allow blank lines between sections
            if sectionRE.exactMatch(qline):  # section start
                section = sectionRE.cap(1)
                argument = unicode(sectionRE.cap(2).trimmed())
                endsec = QString(u"{{/" + section + u"}}")
                if section == u"VERSION":
                    if len(argument) != 0:
                        metaVersion = int(argument)
                    continue  # no more data after {{VERSION x }}
                elif section == u"STALECENSUS":
                    if argument == u"TRUE":
                        IMC.staleCensus = IMC.staleCensusLoaded
                    continue  # no more data after {{STALECENSUS x}}
                elif section == u"NEEDSPELLCHECK":
                    if argument == u"TRUE":
                        IMC.needSpellCheck = True
                    continue  # no more data after {{NEEDSPELLCHECK x}}
                elif section == u"ENCODING":
                    IMC.bookSaveEncoding = QString(argument)
                    continue
                elif section == u"MAINDICT":
                    IMC.bookMainDict = QString(argument)
                    continue
                elif section == u"DOCHASH":
                    IMC.metaHash = argument
                    continue
                elif section == u"PAGETABLE":
                    qline = metaStream.readLine()
                    while (not qline.startsWith(endsec)) and (
                            not qline.isEmpty()):
                        IMC.pageTable.metaStringIn(qline)
                        qline = metaStream.readLine()
                    continue
                elif section == u"CHARCENSUS":
                    qline = metaStream.readLine()
                    while (not qline.startsWith(endsec)) and (
                            not qline.isEmpty()):
                        # can't just .split the char census, the first
                        # char is the char being counted and it can be a space.
                        str = unicode(qline)
                        parts = str[2:].split(' ')
                        IMC.charCensus.append(QString(str[0]), int(parts[0]),
                                              int(parts[1]))
                        qline = metaStream.readLine()
                    continue
                elif section == u"WORDCENSUS":
                    qline = metaStream.readLine()
                    while (not qline.startsWith(endsec)) and (
                            not qline.isEmpty()):
                        parts = unicode(qline).split(' ')
                        IMC.wordCensus.append(QString(parts[0]), int(parts[1]),
                                              int(parts[2]))
                        qline = metaStream.readLine()
                    continue
                elif section == u"BOOKMARKS":
                    qline = metaStream.readLine()
                    while (not qline.startsWith(endsec)) and (
                            not qline.isEmpty()):
                        parts = unicode(qline).split(' ')
                        tc = QTextCursor(self.document())
                        tc.setPosition(int(parts[1]))
                        if len(parts
                               ) == 3:  # early versions didn't save anchor
                            tc.movePosition(int(parts[2]),
                                            QTextCursor.KeepAnchor)
                        self.bookMarkList[int(parts[0])] = tc
                        qline = metaStream.readLine()
                    continue
                elif section == u"NOTES":
                    e = IMC.notesEditor
                    e.setUndoRedoEnabled(False)
                    qline = metaStream.readLine()
                    while (not qline.startsWith(endsec)
                           ) and not metaStream.atEnd():
                        if qline.startsWith(u"\xfffd"):  # escaped {{
                            qline.remove(0, 1)
                        e.appendPlainText(qline)
                        qline = metaStream.readLine()
                    e.setUndoRedoEnabled(True)
                    continue
                elif section == u"GOODWORDS":
                    # not going to bother checking for endsec return,
                    # if it isn't that then we will shortly fail anyway
                    w = IMC.goodWordList.load(metaStream, endsec)
                    continue
                elif section == u"BADWORDS":
                    w = IMC.badWordList.load(metaStream, endsec)
                    continue
                elif section == u"CURSOR":  # restore selection as of save
                    p1p2 = argument.split(' ')
                    tc = QTextCursor(self.document())
                    tc.setPosition(int(p1p2[0]), QTextCursor.MoveAnchor)
                    tc.setPosition(int(p1p2[1]), QTextCursor.KeepAnchor)
                    self.setTextCursor(tc)
                else:
                    # this can't happen; section is text captured by the RE
                    # and we have accounted for all possibilities
                    raise AssertionError, "impossible metadata"
            else:  # Non-blank line that doesn't match sectionRE?
                pqMsgs.infoMsg(
                    "Unexpected line in metadata: {0}".format(
                        pqMsgs.trunc(qline, 20)),
                    "Metadata may be incomplete, suggest quit")
                break

    # Rebuild as much of the char/word census and spellcheck as we need to.
    # This is called from load, above, and from the Char and Word panels
    # Refresh buttons. If page=True we are loading a doc for which there is
    # no metadata file, so cache page definitions; otherwise just skip the
    # page definitions (see doCensus). If the doc has changed we need to
    # rerun the full char/word census. But if not, we might still need a
    # spellcheck, if the dictionary has changed.
    def rebuildMetadata(self, page=False):
        if page or (0 != IMC.staleCensus):
            self.doCensus(page)
        if IMC.needSpellCheck:
            self.doSpellcheck()

    # Go through vocabulary census and check the spelling (it would be a big
    # waste of time to check every word as it was read). If the spellcheck
    # is not up (i.e. it couldn't find a dictionary) we only mark as bad the
    # words in the badwords list.
    def doSpellcheck(self):
        canspell = IMC.spellCheck.isUp()
        nwords = IMC.wordCensus.size()
        if 0 >= nwords:  # could be zero in a null document
            return
        pqMsgs.startBar(nwords, "Checking spelling...")
        for i in range(IMC.wordCensus.size()):
            (qword, cnt, wflags) = IMC.wordCensus.get(i)
            wflags = wflags & (0xff - IMC.WordMisspelt)  # turn off flag if on
            # some words have /dict-tag, split that out as string or ""
            (w, x, d) = unicode(qword).partition("/")
            if IMC.goodWordList.check(w):
                pass
            elif IMC.badWordList.check(w):
                wflags |= IMC.WordMisspelt
            elif canspell:  # check word in its optional dictionary
                if not (IMC.spellCheck.check(w, d)):
                    wflags |= IMC.WordMisspelt
            IMC.wordCensus.setflags(i, wflags)
            if 0 == i & 0x1f:
                pqMsgs.rollBar(i)
        pqMsgs.endBar()
        IMC.needMetadataSave |= IMC.wordlistsChanged
        IMC.needSpellCheck = False
        if IMC.spellingHiliteSwitch:
            self.setHighlight(True)  # force refresh of spell underlines

    # Scan the successive lines of the document and build the census of chars,
    # words, and (first time only) the table of page separators.
    #
    # If this is an HTML file (from IMC.bookType), and if its first line is
    # <!DOCTYPE..., we skip until we see <body>. This avoids polluting our
    # char and word censii with CSS comments and etc. Regular HTML tags
    # like <table> and <b> are skipped over automatically during parsing.
    #
    # Qt obligingly supplies each line as a QTextBlock. We examine the line
    # to see if it is a page separator. If we are opening a file having no
    # metadata, the Page argument is True and we build a page table entry.
    # Other times (e.g. from the Refresh button of the Word or Char panel),
    # we skip over page separator lines.

    # Each non-separator line is first scanned by characters and then for words.
    # The character scan counts characters for the Chars panel. We do NOT parse
    # the text for PGDP productions [oe] and [OE] nor other markups for accented
    # characters such as [=o] for o-with-macron or [^a] for a-with-circumflex.
    # These are just counted as [, o, e, ]. Reasons: (1) the alternative, to parse
    # them into their proper unicode values and count those, entails a whole lotta
    # code that would slow this census badly; (2) having the unicode chars in
    # the Chars panel would be confusing when they are not actually in the text;
    # (3) there is some value in having the counts of [ and ]. For similar reasons
    # we count all the chars in HTML e.g. "<i>" is three characters even though it
    # is effectively unprinted metadata.

    # In scanning words, we collect numbers as words. We collect internal hyphens
    # as letters ("mother-in-law") but not at end of word ("help----" or emdash).
    # We collect internal apostrophes ("it's", "hadn't") but not apostrophes at ends,
    # "'Twas" is counted as "Twas", "students' work" as "students work". This is because
    # there seems to be no way to distinguish the contractive prefix ('Twas)
    # and the final possessive (students') from normal single-quote marks!
    # And we collect leading and internal, but not trailing, square brackets as
    # letters. Thus [OE]dipus and ma[~n]ana are words (but will fail spellcheck)
    # while Einstein[A] (a footnote key) is not.

    # We also collect HTML productions ("</i>" and "<table>") as words. They do not
    # go in the census but we check them for lang= attributes and set the alternate
    # spellcheck dictionary from them.

    def doCensus(self, page=False):
        global reLineSep, reTokens, reLang, qcLess
        # Clear the current census values
        IMC.wordCensus.clear()
        IMC.charCensus.clear()
        # Count chars locally for speed
        local_char_census = defaultdict(int)
        # Name of current alternate dictionary
        alt_dict = QString()  # isEmpty when none
        # Tag from which we set an alternate dict
        alt_dict_tag = QString()
        # Start the progress bar based on the number of lines in the document
        pqMsgs.startBar(self.document().blockCount(),
                        "Counting words and chars...")
        # Find the first text block of interest, skipping an HTML header file
        qtb = self.document().begin()  # first text block
        if IMC.bookType.startsWith(QString(u"htm")) \
        and qtb.text().startsWith(QString(u"<!DOCTYPE")) :
            while (qtb != self.document().end()) \
            and (not qtb.text().startsWith(QString(u"<body"))) :
                qtb = qtb.next()
        # Scan all lines of the document to the end.
        while qtb != self.document().end():
            qsLine = qtb.text()  # text of line as qstring
            dbg = qsLine.size()
            dbg2 = qtb.length()
            if reLineSep.exactMatch(qsLine):  # this is a page separator line
                if page:
                    # We are doing page seps, it's for Open with no .meta seen,
                    # the page table has been cleared. Store the page sep
                    # data in the page table, with a textCursor to its start.
                    qsfilenum = reLineSep.cap(1)  # xxx from "File: xxx.png"
                    qsproofers = reLineSep.cap(2)  # \who\x\blah\etc
                    # proofer names can contain spaces, replace with en-space char
                    qsproofers.replace(QChar(" "), QChar(0x2002))
                    # create a new TextCursor instance
                    tcursor = QTextCursor(self.document())
                    # point it to this text block
                    tcursor.setPosition(qtb.position())
                    # dump all that in the page table
                    IMC.pageTable.loadPsep(tcursor, qsfilenum, qsproofers)
                # else not doing pages, just ignore this psep line
            else:  # not psep, ordinary text line, count chars and words
                pyLine = unicode(qsLine)  # move into Python space to count
                for c in pyLine:
                    local_char_census[c] += 1
                j = 0
                while True:
                    j = reTokens.indexIn(qsLine, j)
                    if j < 0:  # no more word-like units
                        break
                    qsWord = reTokens.cap(0)
                    j += qsWord.size()
                    if qsWord.startsWith(qcLess):
                        # Examine a captured HTML production.
                        if not reTokens.cap(2).isEmpty():
                            # HTML open tag, look for lang='dict'
                            if 0 <= reLang.indexIn(reTokens.cap(3)):
                                # found it: save tag and dict name
                                alt_dict_tag = QString(reTokens.cap(2))
                                alt_dict = QString(reLang.cap(1))
                                alt_dict.prepend(u'/')  # make "/en_GB"
                            # else no lang= attribute
                        else:
                            # HTML close tag, see if it closes alt dict use
                            if reTokens.cap(5) == alt_dict_tag:
                                # yes, matches open-tag for dict, clear it
                                alt_dict_tag = QString()
                                alt_dict = QString()
                            # else no alt dict in use, or didn't match
                    else:  # did not start with "<", process as a word
                        # Set the property flags, which is harder now we don't
                        # look at every character. Use the QString facilities
                        # rather than python because python .isalnum fails
                        # for a hyphenated number "1850-1910".
                        flag = 0
                        if 0 != qsWord.compare(qsWord.toLower()):
                            flag |= IMC.WordHasUpper
                        if 0 != qsWord.compare(qsWord.toUpper()):
                            flag |= IMC.WordHasLower
                        if qsWord.contains(qcHyphen):
                            flag |= IMC.WordHasHyphen
                        if qsWord.contains(qcApostrophe) or qsWord.contains(
                                qcCurlyApostrophe):
                            flag |= IMC.WordHasApostrophe
                        if qsWord.contains(reDigit):
                            flag |= IMC.WordHasDigit
                        IMC.wordCensus.count(qsWord.append(alt_dict), flag)
                # end "while any more words in this line"
            # end of not-a-psep-line processing
            qtb = qtb.next()  # move on to next block
            if (0 == (qtb.blockNumber() & 255)):  #every 256th block
                pqMsgs.rollBar(qtb.blockNumber())  # roll the bar
                QApplication.processEvents()
        # end of scanning all text blocks in the doc
        pqMsgs.endBar()
        # we accumulated the char counts in localCharCensus. Now read it out
        # in sorted order and stick it in the IMC.charCensus list.
        for one_char in sorted(local_char_census.keys()):
            qc = QChar(ord(one_char))  # get to QChar for category() method
            IMC.charCensus.append(QString(qc), local_char_census[one_char],
                                  qc.category())
        IMC.needSpellCheck = True  # after a census this is true
        IMC.staleCensus = 0  # but this is no longer true
        IMC.needMetadataSave |= IMC.wordlistsChanged
コード例 #38
0
class Highlighter(QSyntaxHighlighter):
    """Syntax Highlighter for NINJA-IDE."""

    # braces
    braces = ['\\(', '\\)', '\\{', '\\}', '\\[', '\\]']

    def __init__(self,
                 document,
                 lang=None,
                 scheme=None,
                 errors=None,
                 pep8=None,
                 migration=None):
        QSyntaxHighlighter.__init__(self, document)
        self.highlight_function = self.realtime_highlight
        self.errors = errors
        self.pep8 = pep8
        self.migration = migration
        self._old_search = None
        self.selected_word_lines = []
        self.visible_limits = (0, 50)
        self._styles = {}
        if lang is not None:
            self.apply_highlight(lang, scheme)

    def sanitize(self, word):
        """Sanitize the string to avoid problems with the regex."""
        return word.replace('\\', '\\\\')

    def apply_highlight(self, lang, scheme=None, syntax=None):
        """Set the rules that will decide what to highlight and how."""
        if syntax is None:
            langSyntax = settings.SYNTAX.get(lang, {})
        else:
            langSyntax = syntax
        if scheme is not None:
            restyle(scheme)

        keywords = langSyntax.get('keywords', [])
        operators = langSyntax.get('operators', [])
        extras = langSyntax.get('extras', [])

        rules = []

        # Keyword, operator, brace and extras rules
        keyword_pattern = '(^|[^\w\.]{1})(%s)([^\w]{1}|$)'
        rules += [(keyword_pattern % w, 2, STYLES['keyword'])
                  for w in keywords]
        rules += [(r'%s' % o, 0, STYLES['operator']) for o in operators]
        rules += [(r'%s' % b, 0, STYLES['brace']) for b in Highlighter.braces]
        rules += [(keyword_pattern % e, 2, STYLES['extras']) for e in extras]

        # All other rules
        proper = langSyntax.get('properObject', None)
        if proper is not None:
            proper = r'\b%s\b' % str(proper[0])
            rules += [(proper, 0, STYLES['properObject'])]

        rules.append((r'__\w+__', 0, STYLES['properObject']))

        # Classes and functions
        definition = langSyntax.get('definition', [])
        for de in definition:
            expr = r'\b%s\b\s*(\w+)' % de
            rules.append((expr, 1, STYLES['definition']))

        # Numeric literals
        rules += [
            (r'\b[+-]?[0-9]+[lL]?\b', 0, STYLES['numbers']),
            (r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, STYLES['numbers']),
            (r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0,
             STYLES['numbers']),
        ]

        # Regular expressions
        regex = langSyntax.get('regex', [])
        for reg in regex:
            expr = reg[0]
            color = resources.COLOR_SCHEME['extras']
            style = ''
            if len(reg) > 1:
                if reg[1] in resources.CUSTOM_SCHEME:
                    color = resources.CUSTOM_SCHEME[reg[1]]
                elif reg[1] in resources.COLOR_SCHEME:
                    color = resources.COLOR_SCHEME[reg[1]]
            if len(reg) > 2:
                style = reg[2]
            rules.append((expr, 0, format(color, style)))

        # Strings
        stringChar = langSyntax.get('string', [])
        for sc in stringChar:
            expr = r'"[^"\\]*(\\.[^"\\]*)*"' if sc == '"' \
                else r"'[^'\\]*(\\.[^'\\]*)*'"
            rules.append((expr, 0, STYLES['string']))

        # Comments
        comments = langSyntax.get('comment', [])
        for co in comments:
            expr = co + '[^\\n]*'
            rules.append((expr, 0, STYLES['comment']))

        # Multi-line strings (expression, flag, style)
        # FIXME: The triple-quotes in these two lines will mess up the
        # syntax highlighting from this point onward
        self.tri_single = (QRegExp("'''"), 1, STYLES["string2"])
        self.tri_double = (QRegExp('"""'), 2, STYLES['string2'])

        multi = langSyntax.get('multiline_comment', [])
        if multi:
            self.multi_start = (QRegExp(re.escape(multi['open'])),
                                STYLES['comment'])
            self.multi_end = (QRegExp(re.escape(multi['close'])),
                              STYLES['comment'])
        else:
            self.multi_start = None

        # Build a QRegExp for each pattern
        self.rules = [(QRegExp(pat), index, fmt)
                      for (pat, index, fmt) in rules]
        self.selected_word_pattern = None
        #Apply Highlight to the document... (when colors change)
        self.rehighlight()

    def set_selected_word(self, word, partial=True):
        """Set the word to highlight."""
        # partial = True for new highlighter compatibility
        hl_worthy = len(word) > 2
        if hl_worthy:
            self.selected_word_pattern = QRegExp(r'\b%s\b' %
                                                 self.sanitize(word))
        else:
            self.selected_word_pattern = None

        suffix = "(?![A-Za-z_\d])"
        prefix = "(?<![A-Za-z_\d])"
        word = re.escape(word)
        if not partial:
            word = "%s%s%s" % (prefix, word, suffix)
        lines = []
        pat_find = re.compile(word)
        document = self.document()
        for lineno, text in enumerate(document.toPlainText().splitlines()):
            if hl_worthy and pat_find.search(text):
                lines.append(lineno)
            elif self._old_search and self._old_search.search(text):
                lines.append(lineno)
        # Ask perrito if i don't know what the next line does:
        self._old_search = hl_worthy and pat_find
        self.rehighlight_lines(lines)

    def __highlight_pep8(self, char_format, user_data):
        """Highlight the lines with pep8 errors."""
        user_data.error = True
        char_format = char_format.toCharFormat()
        char_format.setUnderlineColor(
            QColor(
                resources.CUSTOM_SCHEME.get(
                    'pep8-underline',
                    resources.COLOR_SCHEME['pep8-underline'])))
        char_format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
        return char_format

    def __highlight_lint(self, char_format, user_data):
        """Highlight the lines with lint errors."""
        user_data.error = True
        char_format = char_format.toCharFormat()
        char_format.setUnderlineColor(
            QColor(
                resources.CUSTOM_SCHEME.get(
                    'error-underline',
                    resources.COLOR_SCHEME['error-underline'])))
        char_format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
        return char_format

    def __highlight_migration(self, char_format, user_data):
        """Highlight the lines with lint errors."""
        user_data.error = True
        char_format = char_format.toCharFormat()
        char_format.setUnderlineColor(
            QColor(
                resources.CUSTOM_SCHEME.get(
                    'migration-underline',
                    resources.COLOR_SCHEME['migration-underline'])))
        char_format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
        return char_format

    def highlightBlock(self, text):
        """Apply syntax highlighting to the given block of text."""
        self.highlight_function(text)

    def set_open_visible_area(self, is_line, position):
        """Set the range of lines that should be highlighted on open."""
        if is_line:
            self.visible_limits = (position - 50, position + 50)

    def open_highlight(self, text):
        """Only highlight the lines inside the accepted range."""
        if self.visible_limits[0] <= self.currentBlock().blockNumber() <= \
           self.visible_limits[1]:
            self.realtime_highlight(text)
        else:
            self.setCurrentBlockState(0)

    def async_highlight(self):
        """Execute a thread to collect the info of the things to highlight.

        The thread will collect the data from where to where to highlight,
        and which kind of highlight to use for those sections, and return
        that info to the main thread after it process all the file."""
        self.thread_highlight = HighlightParserThread(self)
        self.connect(self.thread_highlight,
                     SIGNAL("highlightingDetected(PyQt_PyObject)"),
                     self._execute_threaded_highlight)
        self.thread_highlight.start()

    def _execute_threaded_highlight(self, styles=None):
        """Function called with the info collected when the thread ends."""
        self.highlight_function = self.threaded_highlight
        if styles:
            self._styles = styles
            lines = list(
                set(styles.keys()) -
                set(range(self.visible_limits[0], self.visible_limits[1])))
            # Highlight the rest of the lines that weren't highlighted on open
            self.rehighlight_lines(lines, False)
        else:
            self._styles = {}
        self.highlight_function = self.realtime_highlight
        self.thread_highlight.wait()

    def threaded_highlight(self, text):
        """Highlight each line using the info collected by the thread.

        This function doesn't need to execute the regular expressions to see
        where the highlighting starts and end for each rule, it just take
        the start and end point, and the proper highlighting style from the
        info returned from the thread and applied that to the document."""
        hls = []
        block = self.currentBlock()
        user_data = syntax_highlighter.get_user_data(block)
        user_data.clear_data()
        block_number = block.blockNumber()
        highlight_errors = lambda cf, ud: cf
        if self.errors and (block_number in self.errors.errorsSummary):
            highlight_errors = self.__highlight_lint
        elif self.pep8 and (block_number in self.pep8.pep8checks):
            highlight_errors = self.__highlight_pep8
        elif self.migration and (block_number
                                 in self.migration.migration_data):
            highlight_errors = self.__highlight_migration

        char_format = block.charFormat()
        char_format = highlight_errors(char_format, user_data)
        self.setFormat(0, len(block.text()), char_format)

        block_styles = self._styles.get(block.blockNumber(), ())
        for index, length, char_format in block_styles:
            char_format = highlight_errors(char_format, user_data)
            if (self.format(index) != STYLES['string']):
                self.setFormat(index, length, char_format)
                if char_format == STYLES['string']:
                    hls.append((index, index + length))
                    user_data.add_str_group(index, index + length)
                elif char_format == STYLES['comment']:
                    user_data.comment_start_at(index)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(
                text,
                *self.tri_single,
                hls=hls,
                highlight_errors=highlight_errors,
                user_data=user_data)
            if not in_multiline:
                in_multiline = self.match_multiline(
                    text,
                    *self.tri_double,
                    hls=hls,
                    highlight_errors=highlight_errors,
                    user_data=user_data)
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        block.setUserData(user_data)

    def realtime_highlight(self, text):
        """Highlight each line while it is being edited.

        This function apply the proper highlight to the line being edited
        by the user, this is a really fast process for each line once you
        already have the document highlighted, but slow to do it the first
        time to highlight all the lines together."""
        hls = []
        block = self.currentBlock()
        user_data = syntax_highlighter.get_user_data(block)
        user_data.clear_data()
        block_number = block.blockNumber()
        highlight_errors = lambda cf, ud: cf
        if self.errors and (block_number in self.errors.errorsSummary):
            highlight_errors = self.__highlight_lint
        elif self.pep8 and (block_number in self.pep8.pep8checks):
            highlight_errors = self.__highlight_pep8
        elif self.migration and (block_number
                                 in self.migration.migration_data):
            highlight_errors = self.__highlight_migration

        char_format = block.charFormat()
        char_format = highlight_errors(char_format, user_data)
        self.setFormat(0, len(block.text()), char_format)

        for expression, nth, char_format in self.rules:
            index = expression.indexIn(text, 0)

            while index >= 0:
                # We actually want the index of the nth match
                index = expression.pos(nth)
                length = len(expression.cap(nth))
                char_format = highlight_errors(char_format, user_data)

                if (self.format(index) != STYLES['string']):
                    self.setFormat(index, length, char_format)
                    if char_format == STYLES['string']:
                        hls.append((index, index + length))
                        user_data.add_str_group(index, index + length)
                    elif char_format == STYLES['comment']:
                        user_data.comment_start_at(index)
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(
                text,
                *self.tri_single,
                hls=hls,
                highlight_errors=highlight_errors,
                user_data=user_data)
            if not in_multiline:
                in_multiline = self.match_multiline(
                    text,
                    *self.tri_double,
                    hls=hls,
                    highlight_errors=highlight_errors,
                    user_data=user_data)
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        #Highlight selected word
        if self.selected_word_pattern is not None:
            index = self.selected_word_pattern.indexIn(text, 0)

            while index >= 0:
                index = self.selected_word_pattern.pos(0)
                length = len(self.selected_word_pattern.cap(0))
                char_format = self.format(index)
                color = STYLES['selectedWord'].foreground().color()
                color.setAlpha(100)
                char_format.setBackground(color)
                self.setFormat(index, length, char_format)
                index = self.selected_word_pattern.indexIn(
                    text, index + length)

        #Spaces
        expression = QRegExp('\s+')
        index = expression.indexIn(text, 0)
        while index >= 0:
            index = expression.pos(0)
            length = len(expression.cap(0))
            char_format = STYLES['spaces']
            char_format = highlight_errors(char_format, user_data)
            self.setFormat(index, length, char_format)
            index = expression.indexIn(text, index + length)

        block.setUserData(user_data)

    def _rehighlight_lines(self, lines):
        """If the document is valid, highlight the list of lines received."""
        if self.document() is None:
            return
        for line in lines:
            block = self.document().findBlockByNumber(line)
            self.rehighlightBlock(block)

    def _get_errors_lines(self):
        """Return the number of lines that contains errors to highlight."""
        errors_lines = []
        block = self.document().begin()
        while block.isValid():
            user_data = syntax_highlighter.get_user_data(block)
            if user_data.error:
                errors_lines.append(block.blockNumber())
            block = block.next()
        return errors_lines

    def rehighlight_lines(self, lines, errors=True):
        """Rehighlight the lines for errors or selected words."""
        if errors:
            errors_lines = self._get_errors_lines()
            refresh_lines = set(lines + errors_lines)
        else:
            refresh_lines = set(lines + self.selected_word_lines)
            self.selected_word_lines = lines
        self._rehighlight_lines(refresh_lines)

    def match_multiline(self,
                        text,
                        delimiter,
                        in_state,
                        style,
                        hls=[],
                        highlight_errors=lambda x: x,
                        user_data=None):
        """Do highlighting of multi-line strings. ``delimiter`` should be a
        ``QRegExp`` for triple-single-quotes or triple-double-quotes, and
        ``in_state`` should be a unique integer to represent the corresponding
        state changes when inside those strings. Returns True if we're still
        inside a multi-line string when this function is finished.
        """
        # If inside triple-single quotes, start at 0
        if self.previousBlockState() == in_state:
            start = 0
            add = 0
        # Otherwise, look for the delimiter on this line
        else:
            start = delimiter.indexIn(text)
            # Move past this match
            add = delimiter.matchedLength()

        # As long as there's a delimiter match on this line...
        while start >= 0:
            # Look for the ending delimiter
            end = delimiter.indexIn(text, start + add)
            # Ending delimiter on this line?
            if end >= add:
                length = end - start + add + delimiter.matchedLength()
                self.setCurrentBlockState(0)
            # No; multi-line string
            else:
                self.setCurrentBlockState(in_state)
                length = len(text) - start + add

            st_fmt = self.format(start)
            start_collides = [pos for pos in hls if pos[0] < start < pos[1]]

            # Apply formatting
            if ((st_fmt != STYLES['comment']) or
               ((st_fmt == STYLES['comment']) and
               (self.previousBlockState() != 0))) and \
                (len(start_collides) == 0):
                style = highlight_errors(style, user_data)
                self.setFormat(start, length, style)
            else:
                self.setCurrentBlockState(0)
            # Look for the next match
            start = delimiter.indexIn(text, start + length)

        # Return True if still inside a multi-line string, False otherwise
        if self.currentBlockState() == in_state:
            return True
        else:
            return False

    def comment_multiline(self, text, delimiter_end, delimiter_start, style):
        """Process the beggining and end of a multiline comment."""
        startIndex = 0
        if self.previousBlockState() != 1:
            startIndex = delimiter_start.indexIn(text)
        while startIndex >= 0:
            endIndex = delimiter_end.indexIn(text, startIndex)
            commentLength = 0
            if endIndex == -1:
                self.setCurrentBlockState(1)
                commentLength = len(text) - startIndex
            else:
                commentLength = endIndex - startIndex + \
                    delimiter_end.matchedLength()

            self.setFormat(startIndex, commentLength, style)
            startIndex = delimiter_start.indexIn(text,
                                                 startIndex + commentLength)
コード例 #39
0
    def realtime_highlight(self, text):
        """Highlight each line while it is being edited.

        This function apply the proper highlight to the line being edited
        by the user, this is a really fast process for each line once you
        already have the document highlighted, but slow to do it the first
        time to highlight all the lines together."""
        hls = []
        block = self.currentBlock()
        user_data = syntax_highlighter.get_user_data(block)
        user_data.clear_data()
        block_number = block.blockNumber()
        highlight_errors = lambda cf, ud: cf
        if self.errors and (block_number in self.errors.errorsSummary):
            highlight_errors = self.__highlight_lint
        elif self.pep8 and (block_number in self.pep8.pep8checks):
            highlight_errors = self.__highlight_pep8
        elif self.migration and (block_number
                                 in self.migration.migration_data):
            highlight_errors = self.__highlight_migration

        char_format = block.charFormat()
        char_format = highlight_errors(char_format, user_data)
        self.setFormat(0, len(block.text()), char_format)

        for expression, nth, char_format in self.rules:
            index = expression.indexIn(text, 0)

            while index >= 0:
                # We actually want the index of the nth match
                index = expression.pos(nth)
                length = len(expression.cap(nth))
                char_format = highlight_errors(char_format, user_data)

                if (self.format(index) != STYLES['string']):
                    self.setFormat(index, length, char_format)
                    if char_format == STYLES['string']:
                        hls.append((index, index + length))
                        user_data.add_str_group(index, index + length)
                    elif char_format == STYLES['comment']:
                        user_data.comment_start_at(index)
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(
                text,
                *self.tri_single,
                hls=hls,
                highlight_errors=highlight_errors,
                user_data=user_data)
            if not in_multiline:
                in_multiline = self.match_multiline(
                    text,
                    *self.tri_double,
                    hls=hls,
                    highlight_errors=highlight_errors,
                    user_data=user_data)
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        #Highlight selected word
        if self.selected_word_pattern is not None:
            index = self.selected_word_pattern.indexIn(text, 0)

            while index >= 0:
                index = self.selected_word_pattern.pos(0)
                length = len(self.selected_word_pattern.cap(0))
                char_format = self.format(index)
                color = STYLES['selectedWord'].foreground().color()
                color.setAlpha(100)
                char_format.setBackground(color)
                self.setFormat(index, length, char_format)
                index = self.selected_word_pattern.indexIn(
                    text, index + length)

        #Spaces
        expression = QRegExp('\s+')
        index = expression.indexIn(text, 0)
        while index >= 0:
            index = expression.pos(0)
            length = len(expression.cap(0))
            char_format = STYLES['spaces']
            char_format = highlight_errors(char_format, user_data)
            self.setFormat(index, length, char_format)
            index = expression.indexIn(text, index + length)

        block.setUserData(user_data)
コード例 #40
0
ファイル: connector.py プロジェクト: olivierdalang/QGIS
        if not self.has_spatial:
            return

        try:
            c = self._execute(None, "SELECT srtext FROM spatial_ref_sys WHERE srid = '%d'" % srid)
        except DbError, e:
            return
        sr = self._fetchone(c)
        self._close_cursor(c)
        if sr is None:
            return

        srtext = sr[0]
        # try to extract just SR name (should be quoted in double quotes)
        regex = QRegExp('"([^"]+)"')
        if regex.indexIn(srtext) > -1:
            srtext = regex.cap(1)
        return srtext

    def isVectorTable(self, table):
        if self.has_geometry_columns and self.has_geometry_columns_access:
            schema, tablename = self.getSchemaTableName(table)
            sql = u"SELECT count(*) FROM geometry_columns WHERE f_table_schema = %s AND f_table_name = %s" % (
                self.quoteString(schema),
                self.quoteString(tablename),
            )

            c = self._execute(None, sql)
            res = self._fetchone(c)
            self._close_cursor(c)
            return res is not None and res[0] > 0
コード例 #41
0
ファイル: highlighter.py プロジェクト: aguzubiaga/ninja-ide
    def realtime_highlight(self, text):
        hls = []
        block = self.currentBlock()
        user_data = block.userData()
        if user_data is None:
            user_data = SyntaxUserData(False)
        user_data.clear_data()
        block_number = block.blockNumber()
        highlight_errors = lambda cf, ud: cf
        if self.errors and (block_number in self.errors.errorsSummary):
            highlight_errors = self.__highlight_lint
        elif self.pep8 and (block_number in self.pep8.pep8checks):
            highlight_errors = self.__highlight_pep8

        char_format = block.charFormat()
        char_format = highlight_errors(char_format, user_data)
        self.setFormat(0, len(block.text()), char_format)

        for expression, nth, char_format in self.rules:
            index = expression.indexIn(text, 0)

            while index >= 0:
                # We actually want the index of the nth match
                index = expression.pos(nth)
                length = expression.cap(nth).length()
                char_format = highlight_errors(char_format, user_data)

                if (self.format(index) != STYLES['string']):
                    self.setFormat(index, length, char_format)
                    if char_format == STYLES['string']:
                        hls.append((index, index + length))
                        user_data.add_str_group(index, index + length)
                    elif char_format == STYLES['comment']:
                        user_data.comment_start_at(index)
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(text, *self.tri_single,
                hls=hls, highlight_errors=highlight_errors,
                user_data=user_data)
            if not in_multiline:
                in_multiline = self.match_multiline(text, *self.tri_double,
                    hls=hls, highlight_errors=highlight_errors,
                    user_data=user_data)
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        #Highlight selected word
        if self.selected_word_pattern is not None:
            index = self.selected_word_pattern.indexIn(text, 0)

            while index >= 0:
                index = self.selected_word_pattern.pos(0)
                length = self.selected_word_pattern.cap(0).length()
                char_format = self.format(index)
                color = QColor()
                color.setNamedColor(STYLES['selectedWord'])
                color.setAlpha(100)
                char_format.setBackground(color)
                self.setFormat(index, length, char_format)
                index = self.selected_word_pattern.indexIn(
                    text, index + length)

        #Spaces
        expression = QRegExp('\s+')
        index = expression.indexIn(text, 0)
        while index >= 0:
            index = expression.pos(0)
            length = expression.cap(0).length()
            char_format = STYLES['spaces']
            if settings.HIGHLIGHT_WHOLE_LINE:
                char_format = highlight_errors(char_format, user_data)
            self.setFormat(index, length, char_format)
            index = expression.indexIn(text, index + length)

        block.setUserData(user_data)
コード例 #42
0
ファイル: highlighter.py プロジェクト: daqing15/ninja-ide
class Highlighter(QSyntaxHighlighter):
    """Syntax Highlighter for NINJA-IDE."""

    # braces
    braces = ["\\(", "\\)", "\\{", "\\}", "\\[", "\\]"]

    def __init__(self, document, lang=None, scheme=None, errors=None, pep8=None, migration=None):
        QSyntaxHighlighter.__init__(self, document)
        self.highlight_function = self.realtime_highlight
        self.errors = errors
        self.pep8 = pep8
        self.migration = migration
        self.selected_word_lines = []
        self.visible_limits = (0, 50)
        self._styles = {}
        if lang is not None:
            self.apply_highlight(lang, scheme)

    def sanitize(self, word):
        """Sanitize the string to avoid problems with the regex."""
        return word.replace("\\", "\\\\")

    def apply_highlight(self, lang, scheme=None, syntax=None):
        """Set the rules that will decide what to highlight and how."""
        if syntax is None:
            langSyntax = settings.SYNTAX.get(lang, {})
        else:
            langSyntax = syntax
        if scheme is not None:
            restyle(scheme)

        keywords = langSyntax.get("keywords", [])
        operators = langSyntax.get("operators", [])
        extras = langSyntax.get("extras", [])

        rules = []

        # Keyword, operator, brace and extras rules
        keyword_pattern = "(^|[^\w\.]{1})(%s)([^\w]{1}|$)"
        rules += [(keyword_pattern % w, 2, STYLES["keyword"]) for w in keywords]
        rules += [(r"%s" % o, 0, STYLES["operator"]) for o in operators]
        rules += [(r"%s" % b, 0, STYLES["brace"]) for b in Highlighter.braces]
        rules += [(keyword_pattern % e, 2, STYLES["extras"]) for e in extras]

        # All other rules
        proper = langSyntax.get("properObject", None)
        if proper is not None:
            proper = r"\b%s\b" % str(proper[0])
            rules += [(proper, 0, STYLES["properObject"])]

        rules.append((r"__\w+__", 0, STYLES["properObject"]))

        # Classes and functions
        definition = langSyntax.get("definition", [])
        for de in definition:
            expr = r"\b%s\b\s*(\w+)" % de
            rules.append((expr, 1, STYLES["definition"]))

        # Numeric literals
        rules += [
            (r"\b[+-]?[0-9]+[lL]?\b", 0, STYLES["numbers"]),
            (r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", 0, STYLES["numbers"]),
            (r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b", 0, STYLES["numbers"]),
        ]

        # Regular expressions
        regex = langSyntax.get("regex", [])
        for reg in regex:
            expr = reg[0]
            color = resources.COLOR_SCHEME["extras"]
            style = ""
            if len(reg) > 1:
                if reg[1] in resources.CUSTOM_SCHEME:
                    color = resources.CUSTOM_SCHEME[reg[1]]
                elif reg[1] in resources.COLOR_SCHEME:
                    color = resources.COLOR_SCHEME[reg[1]]
            if len(reg) > 2:
                style = reg[2]
            rules.append((expr, 0, format(color, style)))

        # Strings
        stringChar = langSyntax.get("string", [])
        for sc in stringChar:
            expr = r'"[^"\\]*(\\.[^"\\]*)*"' if sc == '"' else r"'[^'\\]*(\\.[^'\\]*)*'"
            rules.append((expr, 0, STYLES["string"]))

        # Comments
        comments = langSyntax.get("comment", [])
        for co in comments:
            expr = co + "[^\\n]*"
            rules.append((expr, 0, STYLES["comment"]))

        # Multi-line strings (expression, flag, style)
        # FIXME: The triple-quotes in these two lines will mess up the
        # syntax highlighting from this point onward
        self.tri_single = (QRegExp("'''"), 1, STYLES["string2"])
        self.tri_double = (QRegExp('"""'), 2, STYLES["string2"])

        multi = langSyntax.get("multiline_comment", [])
        if multi:
            self.multi_start = (QRegExp(multi["open"]), STYLES["comment"])
            self.multi_end = (QRegExp(multi["close"]), STYLES["comment"])
        else:
            self.multi_start = None

        # Build a QRegExp for each pattern
        self.rules = [(QRegExp(pat), index, fmt) for (pat, index, fmt) in rules]
        self.selected_word_pattern = None
        # Apply Highlight to the document... (when colors change)
        self.rehighlight()

    def set_selected_word(self, word, partial=True):
        """Set the word to highlight."""
        # partial = True for new highlighter compatibility
        if len(word) > 2:
            self.selected_word_pattern = QRegExp(r"\b%s\b" % self.sanitize(word))
        else:
            self.selected_word_pattern = None

    def __highlight_pep8(self, char_format, user_data):
        """Highlight the lines with pep8 errors."""
        user_data.error = True
        char_format = char_format.toCharFormat()
        char_format.setUnderlineColor(
            QColor(resources.CUSTOM_SCHEME.get("pep8-underline", resources.COLOR_SCHEME["pep8-underline"]))
        )
        char_format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
        return char_format

    def __highlight_lint(self, char_format, user_data):
        """Highlight the lines with lint errors."""
        user_data.error = True
        char_format = char_format.toCharFormat()
        char_format.setUnderlineColor(
            QColor(resources.CUSTOM_SCHEME.get("error-underline", resources.COLOR_SCHEME["error-underline"]))
        )
        char_format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
        return char_format

    def __highlight_migration(self, char_format, user_data):
        """Highlight the lines with lint errors."""
        user_data.error = True
        char_format = char_format.toCharFormat()
        char_format.setUnderlineColor(
            QColor(resources.CUSTOM_SCHEME.get("migration-underline", resources.COLOR_SCHEME["migration-underline"]))
        )
        char_format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
        return char_format

    def highlightBlock(self, text):
        """Apply syntax highlighting to the given block of text."""
        self.highlight_function(text)

    def set_open_visible_area(self, is_line, position):
        """Set the range of lines that should be highlighted on open."""
        if is_line:
            self.visible_limits = (position - 50, position + 50)

    def open_highlight(self, text):
        """Only highlight the lines inside the accepted range."""
        if self.visible_limits[0] <= self.currentBlock().blockNumber() <= self.visible_limits[1]:
            self.realtime_highlight(text)
        else:
            self.setCurrentBlockState(0)

    def async_highlight(self):
        """Execute a thread to collect the info of the things to highlight.

        The thread will collect the data from where to where to highlight,
        and which kind of highlight to use for those sections, and return
        that info to the main thread after it process all the file."""
        self.thread_highlight = HighlightParserThread(self)
        self.connect(
            self.thread_highlight, SIGNAL("highlightingDetected(PyQt_PyObject)"), self._execute_threaded_highlight
        )
        self.thread_highlight.start()

    def _execute_threaded_highlight(self, styles=None):
        """Function called with the info collected when the thread ends."""
        self.highlight_function = self.threaded_highlight
        if styles:
            self._styles = styles
            lines = list(set(styles.keys()) - set(range(self.visible_limits[0], self.visible_limits[1])))
            # Highlight the rest of the lines that weren't highlighted on open
            self.rehighlight_lines(lines, False)
        else:
            self._styles = {}
        self.highlight_function = self.realtime_highlight

    def threaded_highlight(self, text):
        """Highlight each line using the info collected by the thread.

        This function doesn't need to execute the regular expressions to see
        where the highlighting starts and end for each rule, it just take
        the start and end point, and the proper highlighting style from the
        info returned from the thread and applied that to the document."""
        hls = []
        block = self.currentBlock()
        user_data = block.userData()
        if user_data is None:
            user_data = SyntaxUserData(False)
        user_data.clear_data()
        block_number = block.blockNumber()
        highlight_errors = lambda cf, ud: cf
        if self.errors and (block_number in self.errors.errorsSummary):
            highlight_errors = self.__highlight_lint
        elif self.pep8 and (block_number in self.pep8.pep8checks):
            highlight_errors = self.__highlight_pep8
        elif self.migration and (block_number in self.migration.migration_data):
            highlight_errors = self.__highlight_migration

        char_format = block.charFormat()
        char_format = highlight_errors(char_format, user_data)
        self.setFormat(0, len(block.text()), char_format)

        block_styles = self._styles.get(block.blockNumber(), ())
        for index, length, char_format in block_styles:
            char_format = highlight_errors(char_format, user_data)
            if self.format(index) != STYLES["string"]:
                self.setFormat(index, length, char_format)
                if char_format == STYLES["string"]:
                    hls.append((index, index + length))
                    user_data.add_str_group(index, index + length)
                elif char_format == STYLES["comment"]:
                    user_data.comment_start_at(index)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(
                text, *self.tri_single, hls=hls, highlight_errors=highlight_errors, user_data=user_data
            )
            if not in_multiline:
                in_multiline = self.match_multiline(
                    text, *self.tri_double, hls=hls, highlight_errors=highlight_errors, user_data=user_data
                )
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        block.setUserData(user_data)

    def realtime_highlight(self, text):
        """Highlight each line while it is being edited.

        This function apply the proper highlight to the line being edited
        by the user, this is a really fast process for each line once you
        already have the document highlighted, but slow to do it the first
        time to highlight all the lines together."""
        hls = []
        block = self.currentBlock()
        user_data = block.userData()
        if user_data is None:
            user_data = SyntaxUserData(False)
        user_data.clear_data()
        block_number = block.blockNumber()
        highlight_errors = lambda cf, ud: cf
        if self.errors and (block_number in self.errors.errorsSummary):
            highlight_errors = self.__highlight_lint
        elif self.pep8 and (block_number in self.pep8.pep8checks):
            highlight_errors = self.__highlight_pep8
        elif self.migration and (block_number in self.migration.migration_data):
            highlight_errors = self.__highlight_migration

        char_format = block.charFormat()
        char_format = highlight_errors(char_format, user_data)
        self.setFormat(0, len(block.text()), char_format)

        for expression, nth, char_format in self.rules:
            index = expression.indexIn(text, 0)

            while index >= 0:
                # We actually want the index of the nth match
                index = expression.pos(nth)
                length = len(expression.cap(nth))
                char_format = highlight_errors(char_format, user_data)

                if self.format(index) != STYLES["string"]:
                    self.setFormat(index, length, char_format)
                    if char_format == STYLES["string"]:
                        hls.append((index, index + length))
                        user_data.add_str_group(index, index + length)
                    elif char_format == STYLES["comment"]:
                        user_data.comment_start_at(index)
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(
                text, *self.tri_single, hls=hls, highlight_errors=highlight_errors, user_data=user_data
            )
            if not in_multiline:
                in_multiline = self.match_multiline(
                    text, *self.tri_double, hls=hls, highlight_errors=highlight_errors, user_data=user_data
                )
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        # Highlight selected word
        if self.selected_word_pattern is not None:
            index = self.selected_word_pattern.indexIn(text, 0)

            while index >= 0:
                index = self.selected_word_pattern.pos(0)
                length = len(self.selected_word_pattern.cap(0))
                char_format = self.format(index)
                color = STYLES["selectedWord"].foreground().color()
                color.setAlpha(100)
                char_format.setBackground(color)
                self.setFormat(index, length, char_format)
                index = self.selected_word_pattern.indexIn(text, index + length)

        # Spaces
        expression = QRegExp("\s+")
        index = expression.indexIn(text, 0)
        while index >= 0:
            index = expression.pos(0)
            length = len(expression.cap(0))
            char_format = STYLES["spaces"]
            char_format = highlight_errors(char_format, user_data)
            self.setFormat(index, length, char_format)
            index = expression.indexIn(text, index + length)

        block.setUserData(user_data)

    def _rehighlight_lines(self, lines):
        """If the document is valid, highlight the list of lines received."""
        if self.document() is None:
            return
        for line in lines:
            block = self.document().findBlockByNumber(line)
            self.rehighlightBlock(block)

    def _get_errors_lines(self):
        """Return the number of lines that contains errors to highlight."""
        errors_lines = []
        block = self.document().begin()
        while block.isValid():
            user_data = block.userData()
            if (user_data is not None) and (user_data.error):
                errors_lines.append(block.blockNumber())
            block = block.next()
        return errors_lines

    def rehighlight_lines(self, lines, errors=True):
        """Rehighlight the lines for errors or selected words."""
        if errors:
            errors_lines = self._get_errors_lines()
            refresh_lines = set(lines + errors_lines)
        else:
            refresh_lines = set(lines + self.selected_word_lines)
            self.selected_word_lines = lines
        self._rehighlight_lines(refresh_lines)

    def match_multiline(self, text, delimiter, in_state, style, hls=[], highlight_errors=lambda x: x, user_data=None):
        """Do highlighting of multi-line strings. ``delimiter`` should be a
        ``QRegExp`` for triple-single-quotes or triple-double-quotes, and
        ``in_state`` should be a unique integer to represent the corresponding
        state changes when inside those strings. Returns True if we're still
        inside a multi-line string when this function is finished.
        """
        # If inside triple-single quotes, start at 0
        if self.previousBlockState() == in_state:
            start = 0
            add = 0
        # Otherwise, look for the delimiter on this line
        else:
            start = delimiter.indexIn(text)
            # Move past this match
            add = delimiter.matchedLength()

        # As long as there's a delimiter match on this line...
        while start >= 0:
            # Look for the ending delimiter
            end = delimiter.indexIn(text, start + add)
            # Ending delimiter on this line?
            if end >= add:
                length = end - start + add + delimiter.matchedLength()
                self.setCurrentBlockState(0)
            # No; multi-line string
            else:
                self.setCurrentBlockState(in_state)
                length = len(text) - start + add

            st_fmt = self.format(start)
            start_collides = [pos for pos in hls if pos[0] < start < pos[1]]

            # Apply formatting
            if (
                (st_fmt != STYLES["comment"]) or ((st_fmt == STYLES["comment"]) and (self.previousBlockState() != 0))
            ) and (len(start_collides) == 0):
                if user_data is not None:
                    style = highlight_errors(style, user_data)
                self.setFormat(start, length, style)
            else:
                self.setCurrentBlockState(0)
            # Look for the next match
            start = delimiter.indexIn(text, start + length)

        # Return True if still inside a multi-line string, False otherwise
        if self.currentBlockState() == in_state:
            return True
        else:
            return False

    def comment_multiline(self, text, delimiter_end, delimiter_start, style):
        """Process the beggining and end of a multiline comment."""
        startIndex = 0
        if self.previousBlockState() != 1:
            startIndex = delimiter_start.indexIn(text)
        while startIndex >= 0:
            endIndex = delimiter_end.indexIn(text, startIndex)
            commentLength = 0
            if endIndex == -1:
                self.setCurrentBlockState(1)
                commentLength = len(text) - startIndex
            else:
                commentLength = endIndex - startIndex + delimiter_end.matchedLength()

            self.setFormat(startIndex, commentLength, style)
            startIndex = delimiter_start.indexIn(text, startIndex + commentLength)
コード例 #43
0
ファイル: pqEdit.py プロジェクト: tallforasmurf/PPQT
class PPTextEditor(QPlainTextEdit):
    # Initialize the editor on creation.
    def __init__(self, parent=None, fontsize=12 ):
        super(PPTextEditor, self).__init__(parent)
        # Do not allow line-wrap; horizontal scrollbar appears when required.
        self.setLineWrapMode(QPlainTextEdit.NoWrap)
        # make sure when we jump to a line, it goes to the window center
        self.setCenterOnScroll(True)
        # Get a monospaced font as selected by the user with View>Font
        self.setFont(pqMsgs.getMonoFont(fontsize,True))
        # instantiate our "syntax" highlighter object, but link it to an empty
        # QTextDocument. We will redirect it to our actual document only after
        # loading a document, as it relies on metadata, and then only when/if
        # the IMC.*HiliteSwitch es are on.
        self.nulDoc = QTextDocument() # make a null document
        self.hiliter = wordHighLighter(self.nulDoc)
        # all the metadata lists will be initialized when self.clear() is
        # called from pqMain, shortly.
        # save a regex for quickly finding if a selection is a single word
        self.oneWordRE = QRegExp(u'^\W*(\w{2,})\W*$')
        self.menuWord = QString()
        # Create and initialize an SHA-1 hash machine
        self.cuisineart = QCryptographicHash(QCryptographicHash.Sha1)

    # switch on or off our text-highlighting. By switching the highlighter
    # to a null document we remove highlighting; by switching it back to
    # the real document, we cause re-highlighting of everything. This makes
    # significant delay for a large document, so put up a status message
    # during it by starting and ending a progress bar.
    def setHighlight(self, onoff):
        self.hiliter.setDocument(self.nulDoc) # turn off hiliting always
        if onoff:
            pqMsgs.showStatusMsg("Setting Scanno/Spelling Highlights...")
            self.hiliter.setDocument(self.document())
            pqMsgs.clearStatusMsg()

    # Implement clear/new. Just toss everything we keep.
    def clear(self):
        self.setHighlight(False)
        self.document().clear()
        self.document().setModified(False)
        self.bookMarkList = \
            [None, None, None, None, None, None, None, None, None]
        IMC.pageTable.clear()
        IMC.goodWordList.clear()
        IMC.badWordList.clear()
        IMC.wordCensus.clear()
        IMC.charCensus.clear()
        IMC.notesEditor.clear()
        IMC.pngPanel.clear()
        IMC.needSpellCheck = False
        IMC.needMetadataSave = 0x00
        IMC.staleCensus = 0x00
        IMC.bookSaveEncoding = QString(u'UTF-8')
        IMC.bookMainDict = IMC.spellCheck.mainTag
        # force a cursor "move" in order to create a cursorMoved signal that will
        # clear the status line - then undo it so the document isn't modified.
        self.textCursor().insertText(QString(' '))
        self.document().undo()


    # Implement the Edit menu items:
    # Edit > ToUpper,  Edit > ToTitle,  Edit > ToLower
    # Note that in full Unicode, changing letter case is not so simple as it
    # was in Latin-1! We use the QChar and QString facilities to do it, and
    # a regex in a loop to pick off words. Restore the current selection after
    # so another operation can be done on it.
    # N.B. it is not possible to do self.textCursor().setPosition(), it seems
    # that self.textCursor() is "const". One has to create a new cursor,
    # position it, and install it on the document with self.setTextCursor().
    def toUpperCase(self):
        global reWord
        tc = QTextCursor(self.textCursor())
        if not tc.hasSelection() :
            return # no selection, nothing to do
        startpos = tc.selectionStart()
        endpos = tc.selectionEnd()
        qs = QString(tc.selectedText()) # copy of selected text
        i = reWord.indexIn(qs,0) # index of first word if any
        if i < 0 : return # no words in selection, exit
        while i >= 0:
            w = reWord.cap(0) # found word as QString
            n = w.size() # its length
            qs.replace(i,n,w.toUpper()) # replace it with UC version
            i = reWord.indexIn(qs,i+n) # find next word if any
        # we have changed at least one word, replace selection with altered text
        tc.insertText(qs)
        # that wiped the selection, so restore it by "dragging" left to right
        tc.setPosition(startpos,QTextCursor.MoveAnchor) # click
        tc.setPosition(endpos,QTextCursor.KeepAnchor)   # drag
        self.setTextCursor(tc)

    # to-lower is identical except for the method call.
    def toLowerCase(self):
        global reWord # the regex \b\w+\b
        tc = QTextCursor(self.textCursor())
        if not tc.hasSelection() :
            return # no selection, nothing to do
        startpos = tc.selectionStart()
        endpos = tc.selectionEnd()
        qs = QString(tc.selectedText()) # copy of selected text
        i = reWord.indexIn(qs,0) # index of first word if any
        if i < 0 : return # no words in selection, exit
        while i >= 0:
            w = reWord.cap(0) # found word as QString
            n = w.size() # its length
            qs.replace(i,n,w.toLower()) # replace it with UC version
            i = reWord.indexIn(qs,i+n) # find next word if any
        # we have changed at least one word, replace selection with altered text
        tc.insertText(qs)
        # that wiped the selection, so restore it by "dragging" left to right
        tc.setPosition(startpos,QTextCursor.MoveAnchor) # click
        tc.setPosition(endpos,QTextCursor.KeepAnchor)   # drag
        self.setTextCursor(tc)

    # toTitle is similar but we have to change the word to lowercase (in case
    # it is uppercase now) and then change the initial character to upper.
    # Note it would be possible to write a smarter version that looked up the
    # word in a list of common adjectives, connectives, and adverbs and avoided
    # capitalizing a, and, of, by and so forth. Not gonna happen.
    def toTitleCase(self):
        global reWord # the regex \b\w+\b
        self.toLowerCase()
        tc = QTextCursor(self.textCursor())
        if not tc.hasSelection() :
            return # no selection, nothing to do
        startpos = tc.selectionStart()
        endpos = tc.selectionEnd()
        qs = QString(tc.selectedText()) # copy of selected text
        i = reWord.indexIn(qs,0) # index of first word if any
        if i < 0 : return # no words in selection, exit
        while i >= 0:
            w = reWord.cap(0) # found word as QString
            n = w.size()
            qs.replace(i,1,qs.at(i).toUpper()) # replace initial with UC
            i = reWord.indexIn(qs,i+n) # find next word if any
        # we have changed at least one word, replace selection with altered text
        tc.insertText(qs)
        # that wiped the selection, so restore it by "dragging" left to right
        tc.setPosition(startpos,QTextCursor.MoveAnchor) # click
        tc.setPosition(endpos,QTextCursor.KeepAnchor)   # drag
        self.setTextCursor(tc)

    # Re-implement the parent's keyPressEvent in order to provide some
    # special controls. (Note on Mac, "ctrl-" is "cmd-" and "alt-" is "opt-")
    # ctrl-plus increases the edit font size 1 pt
    # (n.b. ctrl-plus probably only comes from a keypad, we usually just get
    #  ctrl-shift-equals instead of plus)
    # ctrl-minus decreases the edit font size 1 pt
    # ctrl-<n> for n in 1..9 jumps the insertion point to bookmark <n>
    # ctrl-shift-<n> extends the selection to bookmark <n>
    # ctrl-alt-<n> sets bookmark n at the current position
    def keyPressEvent(self, event):
        #pqMsgs.printKeyEvent(event)
        kkey = int( int(event.modifiers()) & IMC.keypadDeModifier) | int(event.key())
        # add as little overhead as possible: if it isn't ours, pass it on.
        if kkey in IMC.keysOfInterest : # we trust python to do this quickly
            event.accept() # we handle this one
            if kkey in IMC.findKeys:
                # ^f, ^g, etc. -- just pass them straight to the Find panel
                self.emit(SIGNAL("editKeyPress"),kkey)
            elif kkey in IMC.zoomKeys :
                # n.b. the self.font and setFont methods inherit from QWidget
                # Point increment by which to change.
                n = (-1) if (kkey == IMC.ctl_minus) else 1
                # Actual point size currently in use, plus increment
                p = self.fontInfo().pointSize() + n
                if (p > 3) and (p < 65): # don't let's get ridiculous, hmm?
                    # Simply calling self.font().setPointSize() had no effect,
                    # we have to actually call setFont() to make change happen.
                    f = self.font() # so get our font,
                    f.setPointSize(p) # change its point size +/-
                    self.setFont(f) # and put the font back
                    IMC.fontSize = p # and remember the size for shutdown time
            elif kkey in IMC.markKeys : # ^1-9, jump to bookmark
                bkn = kkey - IMC.ctl_1 # make it 0-8
                if self.bookMarkList[bkn] is not None: # if that bookmark is set,
                    self.setTextCursor(self.bookMarkList[bkn]) # jump to it
            elif kkey in IMC.markShiftKeys : # shift-ctl-1/9, select to mark
                # Make our document cursor's selection go from our current ANCHOR
                # to the POSITION from the bookmark cursor.
                mark_tc = self.bookMarkList[kkey - IMC.ctl_shft_1]
                if mark_tc is not None:
                    tc = QTextCursor(self.textCursor())
                    tc.setPosition(mark_tc.position(),QTextCursor.KeepAnchor)
                    self.setTextCursor(tc)
            elif kkey in IMC.markSetKeys : # ctl-alt-1-9, set a bookmark
                bkn = kkey - IMC.ctl_alt_1 # make it 0-8
                self.bookMarkList[bkn] = QTextCursor(self.textCursor())
                IMC.needMetadataSave |= IMC.bookmarksChanged
        else: # not in keysOfInterest, so pass it up to parent
            event.ignore()
            super(PPTextEditor, self).keyPressEvent(event)

    # Called from pqFind after doing a successful search, this method centers the
    # current selection (which is the result of the find) in the window. If the selection
    # is large, put the top of the selection higher than center but on no account
    # above the top of the viewport. Two problems arise: One, the rectangles returned
    # by .cursorRect() and by .viewport().geometry() are in pixel units, while the
    # vertical scrollbar is sized in logical text lines. So we work out the adjustment
    # as a fraction of the viewport, times the scrollbar's pageStep value to get lines.
    # Two, cursorRect gives only the height of the actual cursor, not of the selected
    # text. To find out the height of the full selection we have to get a cursorRect
    # for the start of the selection, and another for the end of it.
    def centerCursor(self) :
        tc = QTextCursor(self.textCursor()) # copy the working cursor with its selection
        top_point = tc.position() # one end of selection, in character units
        bot_point = tc.anchor() # ..and the other end
        if top_point > bot_point : # often the position is > the anchor
            (top_point, bot_point) = (bot_point, top_point)
        tc.setPosition(top_point) # cursor for the top of the selection
        selection_top = self.cursorRect(tc).top() # ..get its top pixel
        line_height = self.cursorRect(tc).height() # and save height of one line
        tc.setPosition(bot_point) # cursor for the end of the selection
        selection_bot = self.cursorRect(tc).bottom() # ..selection's bottom pixel
        selection_height = selection_bot - selection_top + 1 # selection height in pixels
        view_height = self.viewport().geometry().height() # scrolled area's height in px
        view_half = view_height >> 1 # int(view_height/2)
        pixel_adjustment = 0
        if selection_height < view_half :
            # selected text is less than half the window height: center the top of the
            # selection, i.e., make the cursor_top equal to view_half.
            pixel_adjustment = selection_top - view_half # may be negative
        else :
            # selected text is taller than half the window, can we show it all?
            if selection_height < (view_height - line_height) :
                # all selected text fits in the viewport (with a little free): center it.
                pixel_adjustment = (selection_top + (selection_height/2)) - view_half
            else :
                # not all selected text fits the window, put text top near window top
                pixel_adjustment = selection_top - line_height
        # OK, convert the pixel adjustment to a line-adjustment based on the assumption
        # that a scrollbar pageStep is the height of the viewport in lines.
        adjust_fraction = pixel_adjustment / view_height
        vscroller = self.verticalScrollBar()
        page_step = vscroller.pageStep() # lines in a viewport page, actually less 1
        adjust_lines = int(page_step * adjust_fraction)
        target = vscroller.value() + adjust_lines
        if (target >= 0) and (target <= vscroller.maximum()) :
            vscroller.setValue(target)



    # Catch the contextMenu event and extend the standard context menu with
    # a separator and the option to add a word to good-words, but only when
    # there is a selection and it encompasses just one word.
    def contextMenuEvent(self,event) :
        ctx_menu = self.createStandardContextMenu()
        if self.textCursor().hasSelection :
            qs = self.textCursor().selectedText()
            if 0 == self.oneWordRE.indexIn(qs) : # it matches at 0 or not at all
                self.menuWord = self.oneWordRE.cap(1) # save the word
                ctx_menu.addSeparator()
                gw_name = QString(self.menuWord) # make a copy
                gw_action = ctx_menu.addAction(gw_name.append(QString(u' -> Goodwords')))
                self.connect(gw_action, SIGNAL("triggered()"), self.addToGW)
        ctx_menu.exec_(event.globalPos())

    # This slot receives the "someword -> good_words" context menu action
    def addToGW(self) :
        IMC.goodWordList.insert(self.menuWord)
        IMC.needMetadataSave |= IMC.goodwordsChanged
        IMC.needSpellCheck = True
        IMC.mainWindow.setWinModStatus()

    # Implement save: the main window opens the files for output using
    # QIODevice::WriteOnly, which wipes the contents (contrary to the doc)
    # so we need to write the document and metadata regardless of whether
    # they've been modified. However we avoid rebuilding metadata if we can.
    def save(self, dataStream, metaStream):
        # Get the contents of the document as a QString
        doc_text = self.toPlainText()
        # Calculate the SHA-1 hash over the document and save it in both hash
        # fields of the IMC.
        self.cuisineart.reset()
        self.cuisineart.addData(doc_text)
        IMC.metaHash = IMC.documentHash = bytes(self.cuisineart.result()).__repr__()
        # write the document, which is pretty simple in the QStream world
        dataStream << doc_text
        dataStream.flush()
        #self.rebuildMetadata() # update any census that needs it
        self.writeMetadata(metaStream)
        metaStream.flush()
        IMC.needMetadataSave = 0x00
        self.document().setModified(False) # this triggers main.setWinModStatus()

    def writeMetadata(self,metaStream):
        # Writing the metadata takes a bit more work.
        # pageTable goes out between {{PAGETABLE}}..{{/PAGETABLE}}
        metaStream << u"{{VERSION 0}}\n" # meaningless at the moment
        metaStream << u"{{ENCODING "
        metaStream << unicode(IMC.bookSaveEncoding)
        metaStream << u"}}\n"
        metaStream << u"{{STALECENSUS "
        if 0 == IMC.staleCensus :
            metaStream << u"FALSE"
        else:
            metaStream << u"TRUE"
        metaStream << u"}}\n"
        metaStream << u"{{NEEDSPELLCHECK "
        if 0 == IMC.needSpellCheck :
            metaStream << u"FALSE"
        else:
            metaStream << u"TRUE"
        metaStream << u"}}\n"
        metaStream << u"{{MAINDICT "
        metaStream << unicode(IMC.bookMainDict)
        metaStream << u"}}\n"
        # The hash could contain any character. Using __repr__ ensured
        # it is enclosed in balanced single or double quotes but to be
        # double sure we will fence it in characters we can spot with a regex.
        metaStream << u"{{DOCHASH " + IMC.documentHash + u" }}\n"
        if IMC.pageTable.size() :
            metaStream << u"{{PAGETABLE}}\n"
            for i in range(IMC.pageTable.size()) :
                metaStream << IMC.pageTable.metaStringOut(i)
            metaStream << u"{{/PAGETABLE}}\n"
        if IMC.charCensus.size() :
            metaStream << u"{{CHARCENSUS}}\n"
            for i in range(IMC.charCensus.size()):
                (w,n,f) = IMC.charCensus.get(i)
                metaStream << "{0} {1} {2}\n".format(unicode(w), n, f)
            metaStream << u"{{/CHARCENSUS}}\n"
        if IMC.wordCensus.size() :
            metaStream << u"{{WORDCENSUS}}\n"
            for i in range(IMC.wordCensus.size()):
                (w,n,f) = IMC.wordCensus.get(i)
                metaStream << "{0} {1} {2}\n".format(unicode(w), n, f)
            metaStream << u"{{/WORDCENSUS}}\n"
        metaStream << u"{{BOOKMARKS}}\n"
        for i in range(9): # 0..8
            if self.bookMarkList[i] is not None :
                metaStream << "{0} {1} {2}\n".format(i,self.bookMarkList[i].position(),self.bookMarkList[i].anchor())
        metaStream << u"{{/BOOKMARKS}}\n"
        metaStream << u"{{NOTES}}\n"
        d = IMC.notesEditor.document()
        if not d.isEmpty():
            for i in range( d.blockCount() ):
                t = d.findBlockByNumber(i).text()
                if t.startsWith("{{"):
                    t.prepend(u"\xfffd") # Unicode Replacement char
                metaStream << t + "\n"
            IMC.notesEditor.document().setModified(False)
        metaStream << u"{{/NOTES}}\n"
        if IMC.goodWordList.active() : # have some good words
            metaStream << u"{{GOODWORDS}}\n"
            IMC.goodWordList.save(metaStream)
            metaStream << u"{{/GOODWORDS}}\n"
        if IMC.badWordList.active() : # have some bad words
            metaStream << u"{{BADWORDS}}\n"
            IMC.badWordList.save(metaStream)
            metaStream << u"{{/BADWORDS}}\n"
        p1 = self.textCursor().selectionStart()
        p2 = self.textCursor().selectionEnd()
        metaStream << u"{{CURSOR "+unicode(p1)+u' '+unicode(p2)+u"}}\n"
        metaStream.flush()

    # Implement load: the main window has the job of finding and opening files
    # then passes QTextStreams ready to read here. If metaStream is None,
    # no metadata file was found and we construct the metadata.
    # n.b. before main calls here, it calls our .clear, hence lists are
    # empty, hiliting is off, etc.

    def load(self, dataStream, metaStream, goodStream, badStream):
        # Load the document file into the editor
        self.setPlainText(dataStream.readAll())
        # Initialize the hash value for the document, which will be equal unless
        # we read something different from the metadata file.
        self.cuisineart.reset()
        self.cuisineart.addData(self.toPlainText())
        IMC.metaHash = IMC.documentHash = bytes(self.cuisineart.result()).__repr__()
        if metaStream is None:
            # load goodwords, badwords, and take census
            if goodStream is not None:
                IMC.goodWordList.load(goodStream)
            if badStream is not None:
                IMC.badWordList.load(badStream)
            self.rebuildMetadata(page=True) # build page table & vocab from scratch
        else:
            self.loadMetadata(metaStream)
        # If the metaData and document hashes now disagree, it is because the metadata
        # had a DOCHASH value for a different book or version. Warn the user.
        if IMC.metaHash != IMC.documentHash :
            pqMsgs.warningMsg(u"The document file and metadata file do not match!",
                              u"Bookmarks, page breaks and other metadata will be wrong! Strongly recommend you not edit or save this book.")
        # restore hiliting if the user wanted it. Note this can cause a
        # serious delay if the new book is large. However the alternative is
        # to not set it on and then we are out of step with the View menu
        # toggles, so the user has to set it off before loading, or suffer.
        self.setHighlight(IMC.scannoHiliteSwitch or IMC.spellingHiliteSwitch)
        # set a different main dict if there was one in the metadata
        if IMC.bookMainDict is not None:
            IMC.spellCheck.setMainDict(IMC.bookMainDict)

    # load page table & vocab from the .meta file as a stream.
    # n.b. QString has a split method we could use but instead
    # we take the input line to a Python u-string and split it. For
    # the word/char census we have to take the key back to a QString.
    def loadMetadata(self,metaStream):
        sectionRE = QRegExp( u"\{\{(" + '|'.join (
            ['PAGETABLE','CHARCENSUS','WORDCENSUS','BOOKMARKS',
             'NOTES','GOODWORDS','BADWORDS','CURSOR','VERSION',
             'STALECENSUS','NEEDSPELLCHECK','ENCODING', 'DOCHASH', 'MAINDICT'] ) \
                             + u")(.*)\}\}",
            Qt.CaseSensitive)
        metaVersion = 0 # base version
        while not metaStream.atEnd() :
            qline = metaStream.readLine().trimmed()
            if qline.isEmpty() : continue # allow blank lines between sections
            if sectionRE.exactMatch(qline) : # section start
                section = sectionRE.cap(1)
                argument = unicode(sectionRE.cap(2).trimmed())
                endsec = QString(u"{{/" + section + u"}}")
                if section == u"VERSION":
                    if len(argument) != 0 :
                        metaVersion = int(argument)
                    continue # no more data after {{VERSION x }}
                elif section == u"STALECENSUS" :
                    if argument == u"TRUE" :
                        IMC.staleCensus = IMC.staleCensusLoaded
                    continue # no more data after {{STALECENSUS x}}
                elif section == u"NEEDSPELLCHECK" :
                    if argument == u"TRUE" :
                        IMC.needSpellCheck = True
                    continue # no more data after {{NEEDSPELLCHECK x}}
                elif section == u"ENCODING" :
                    IMC.bookSaveEncoding = QString(argument)
                    continue
                elif section == u"MAINDICT" :
                    IMC.bookMainDict = QString(argument)
                    continue
                elif section == u"DOCHASH" :
                    IMC.metaHash = argument
                    continue
                elif section == u"PAGETABLE":
                    qline = metaStream.readLine()
                    while (not qline.startsWith(endsec)) and (not qline.isEmpty()):
                        IMC.pageTable.metaStringIn(qline)
                        qline = metaStream.readLine()
                    continue
                elif section == u"CHARCENSUS":
                    qline = metaStream.readLine()
                    while (not qline.startsWith(endsec)) and (not qline.isEmpty()):
                        # can't just .split the char census, the first
                        # char is the char being counted and it can be a space.
                        str = unicode(qline)
                        parts = str[2:].split(' ')
                        IMC.charCensus.append(QString(str[0]),int(parts[0]),int(parts[1]))
                        qline = metaStream.readLine()
                    continue
                elif section == u"WORDCENSUS":
                    qline = metaStream.readLine()
                    while (not qline.startsWith(endsec)) and (not qline.isEmpty()):
                        parts = unicode(qline).split(' ')
                        IMC.wordCensus.append(QString(parts[0]),int(parts[1]),int(parts[2]))
                        qline = metaStream.readLine()
                    continue
                elif section == u"BOOKMARKS":
                    qline = metaStream.readLine()
                    while (not qline.startsWith(endsec)) and (not qline.isEmpty()):
                        parts = unicode(qline).split(' ')
                        tc = QTextCursor(self.document() )
                        tc.setPosition(int(parts[1]))
                        if len(parts) == 3 : # early versions didn't save anchor
                            tc.movePosition(int(parts[2]),QTextCursor.KeepAnchor)
                        self.bookMarkList[int(parts[0])] = tc
                        qline = metaStream.readLine()
                    continue
                elif section == u"NOTES":
                    e = IMC.notesEditor
                    e.setUndoRedoEnabled(False)
                    qline = metaStream.readLine()
                    while (not qline.startsWith(endsec)) and not metaStream.atEnd():
                        if qline.startsWith(u"\xfffd"): # escaped {{
                            qline.remove(0,1)
                        e.appendPlainText(qline)
                        qline = metaStream.readLine()
                    e.setUndoRedoEnabled(True)
                    continue
                elif section == u"GOODWORDS" :
                    # not going to bother checking for endsec return,
                    # if it isn't that then we will shortly fail anyway
                    w = IMC.goodWordList.load(metaStream,endsec)
                    continue
                elif section == u"BADWORDS" :
                    w = IMC.badWordList.load(metaStream,endsec)
                    continue
                elif section == u"CURSOR" : # restore selection as of save
                    p1p2 = argument.split(' ')
                    tc = QTextCursor(self.document())
                    tc.setPosition(int(p1p2[0]),QTextCursor.MoveAnchor)
                    tc.setPosition(int(p1p2[1]),QTextCursor.KeepAnchor)
                    self.setTextCursor(tc)
                else:
                    # this can't happen; section is text captured by the RE
                    # and we have accounted for all possibilities
                    raise AssertionError, "impossible metadata"
            else: # Non-blank line that doesn't match sectionRE?
                pqMsgs.infoMsg(
                    "Unexpected line in metadata: {0}".format(pqMsgs.trunc(qline,20)),
                        "Metadata may be incomplete, suggest quit")
                break

    # Rebuild as much of the char/word census and spellcheck as we need to.
    # This is called from load, above, and from the Char and Word panels
    # Refresh buttons. If page=True we are loading a doc for which there is
    # no metadata file, so cache page definitions; otherwise just skip the
    # page definitions (see doCensus). If the doc has changed we need to
    # rerun the full char/word census. But if not, we might still need a
    # spellcheck, if the dictionary has changed.
    def rebuildMetadata(self,page=False):
        if page or (0 != IMC.staleCensus) :
            self.doCensus(page)
        if IMC.needSpellCheck :
            self.doSpellcheck()

    # Go through vocabulary census and check the spelling (it would be a big
    # waste of time to check every word as it was read). If the spellcheck
    # is not up (i.e. it couldn't find a dictionary) we only mark as bad the
    # words in the badwords list.
    def doSpellcheck(self):
        canspell = IMC.spellCheck.isUp()
        nwords = IMC.wordCensus.size()
        if 0 >= nwords : # could be zero in a null document
            return
        pqMsgs.startBar(nwords,"Checking spelling...")
        for i in range(IMC.wordCensus.size()):
            (qword, cnt, wflags) = IMC.wordCensus.get(i)
            wflags = wflags & (0xff - IMC.WordMisspelt) # turn off flag if on
            # some words have /dict-tag, split that out as string or ""
            (w,x,d) = unicode(qword).partition("/")
            if IMC.goodWordList.check(w):
                pass
            elif IMC.badWordList.check(w) :
                wflags |= IMC.WordMisspelt
            elif canspell : # check word in its optional dictionary
                if not ( IMC.spellCheck.check(w,d) ) :
                    wflags |= IMC.WordMisspelt
            IMC.wordCensus.setflags(i,wflags)
            if 0 == i & 0x1f :
                pqMsgs.rollBar(i)
        pqMsgs.endBar()
        IMC.needMetadataSave |= IMC.wordlistsChanged
        IMC.needSpellCheck = False
        if IMC.spellingHiliteSwitch :
            self.setHighlight(True) # force refresh of spell underlines

    # Scan the successive lines of the document and build the census of chars,
    # words, and (first time only) the table of page separators.
    #
    # If this is an HTML file (from IMC.bookType), and if its first line is
    # <!DOCTYPE..., we skip until we see <body>. This avoids polluting our
    # char and word censii with CSS comments and etc. Regular HTML tags
    # like <table> and <b> are skipped over automatically during parsing.
    #
    # Qt obligingly supplies each line as a QTextBlock. We examine the line
    # to see if it is a page separator. If we are opening a file having no
    # metadata, the Page argument is True and we build a page table entry.
    # Other times (e.g. from the Refresh button of the Word or Char panel),
    # we skip over page separator lines.

    # Each non-separator line is first scanned by characters and then for words.
    # The character scan counts characters for the Chars panel. We do NOT parse
    # the text for PGDP productions [oe] and [OE] nor other markups for accented
    # characters such as [=o] for o-with-macron or [^a] for a-with-circumflex.
    # These are just counted as [, o, e, ]. Reasons: (1) the alternative, to parse
    # them into their proper unicode values and count those, entails a whole lotta
    # code that would slow this census badly; (2) having the unicode chars in
    # the Chars panel would be confusing when they are not actually in the text;
    # (3) there is some value in having the counts of [ and ]. For similar reasons
    # we count all the chars in HTML e.g. "<i>" is three characters even though it
    # is effectively unprinted metadata.

    # In scanning words, we collect numbers as words. We collect internal hyphens
    # as letters ("mother-in-law") but not at end of word ("help----" or emdash).
    # We collect internal apostrophes ("it's", "hadn't") but not apostrophes at ends,
    # "'Twas" is counted as "Twas", "students' work" as "students work". This is because
    # there seems to be no way to distinguish the contractive prefix ('Twas)
    # and the final possessive (students') from normal single-quote marks!
    # And we collect leading and internal, but not trailing, square brackets as
    # letters. Thus [OE]dipus and ma[~n]ana are words (but will fail spellcheck)
    # while Einstein[A] (a footnote key) is not.

    # We also collect HTML productions ("</i>" and "<table>") as words. They do not
    # go in the census but we check them for lang= attributes and set the alternate
    # spellcheck dictionary from them.

    def doCensus(self, page=False) :
        global reLineSep, reTokens, reLang, qcLess
        # Clear the current census values
        IMC.wordCensus.clear()
        IMC.charCensus.clear()
        # Count chars locally for speed
        local_char_census = defaultdict(int)
        # Name of current alternate dictionary
        alt_dict = QString() # isEmpty when none
        # Tag from which we set an alternate dict
        alt_dict_tag = QString()
        # Start the progress bar based on the number of lines in the document
        pqMsgs.startBar(self.document().blockCount(),"Counting words and chars...")
        # Find the first text block of interest, skipping an HTML header file
        qtb = self.document().begin() # first text block
        if IMC.bookType.startsWith(QString(u"htm")) \
        and qtb.text().startsWith(QString(u"<!DOCTYPE")) :
            while (qtb != self.document().end()) \
            and (not qtb.text().startsWith(QString(u"<body"))) :
                qtb = qtb.next()
        # Scan all lines of the document to the end.
        while qtb != self.document().end() :
            qsLine = qtb.text() # text of line as qstring
            dbg = qsLine.size()
            dbg2 = qtb.length()
            if reLineSep.exactMatch(qsLine): # this is a page separator line
                if page :
                    # We are doing page seps, it's for Open with no .meta seen,
                    # the page table has been cleared. Store the page sep
                    # data in the page table, with a textCursor to its start.
                    qsfilenum = reLineSep.cap(1) # xxx from "File: xxx.png"
                    qsproofers = reLineSep.cap(2) # \who\x\blah\etc
                    # proofer names can contain spaces, replace with en-space char
                    qsproofers.replace(QChar(" "),QChar(0x2002))
                    # create a new TextCursor instance
                    tcursor = QTextCursor(self.document())
                    # point it to this text block
                    tcursor.setPosition(qtb.position())
                    # dump all that in the page table
                    IMC.pageTable.loadPsep(tcursor, qsfilenum, qsproofers)
                # else not doing pages, just ignore this psep line
            else: # not psep, ordinary text line, count chars and words
                pyLine = unicode(qsLine) # move into Python space to count
                for c in pyLine :
                    local_char_census[c] += 1
                j = 0
                while True:
                    j = reTokens.indexIn(qsLine,j)
                    if j < 0 : # no more word-like units
                        break
                    qsWord = reTokens.cap(0)
                    j += qsWord.size()
                    if qsWord.startsWith(qcLess) :
                        # Examine a captured HTML production.
                        if not reTokens.cap(2).isEmpty() :
                            # HTML open tag, look for lang='dict'
                            if 0 <= reLang.indexIn(reTokens.cap(3)) :
                                # found it: save tag and dict name
                                alt_dict_tag = QString(reTokens.cap(2))
                                alt_dict = QString(reLang.cap(1))
                                alt_dict.prepend(u'/') # make "/en_GB"
                            # else no lang= attribute
                        else:
                            # HTML close tag, see if it closes alt dict use
                            if reTokens.cap(5) == alt_dict_tag :
                                # yes, matches open-tag for dict, clear it
                                alt_dict_tag = QString()
                                alt_dict = QString()
                            # else no alt dict in use, or didn't match
                    else : # did not start with "<", process as a word
                        # Set the property flags, which is harder now we don't
                        # look at every character. Use the QString facilities
                        # rather than python because python .isalnum fails
                        # for a hyphenated number "1850-1910".
                        flag = 0
                        if 0 != qsWord.compare(qsWord.toLower()) :
                            flag |= IMC.WordHasUpper
                        if 0 != qsWord.compare(qsWord.toUpper()) :
                            flag |= IMC.WordHasLower
                        if qsWord.contains(qcHyphen) :
                            flag |= IMC.WordHasHyphen
                        if qsWord.contains(qcApostrophe) or qsWord.contains(qcCurlyApostrophe) :
                            flag |= IMC.WordHasApostrophe
                        if qsWord.contains(reDigit) :
                            flag |= IMC.WordHasDigit
                        IMC.wordCensus.count(qsWord.append(alt_dict),flag)
                # end "while any more words in this line"
            # end of not-a-psep-line processing
            qtb = qtb.next() # move on to next block
            if (0 == (qtb.blockNumber() & 255)) : #every 256th block
                pqMsgs.rollBar(qtb.blockNumber()) # roll the bar
                QApplication.processEvents()
        # end of scanning all text blocks in the doc
        pqMsgs.endBar()
        # we accumulated the char counts in localCharCensus. Now read it out
        # in sorted order and stick it in the IMC.charCensus list.
        for one_char in sorted(local_char_census.keys()):
            qc = QChar(ord(one_char)) # get to QChar for category() method
            IMC.charCensus.append(QString(qc),local_char_census[one_char],qc.category())
        IMC.needSpellCheck = True # after a census this is true
        IMC.staleCensus = 0 # but this is no longer true
        IMC.needMetadataSave |= IMC.wordlistsChanged
コード例 #44
0
ファイル: highlighter.py プロジェクト: uKev/ninja-ide
    def realtime_highlight(self, text):
        hls = []
        block = self.currentBlock()
        block_number = block.blockNumber()
        highlight_errors = self.__clean_error
        if self.errors and (block_number in self.errors.errorsSummary):
            highlight_errors = self.__highlight_lint
        elif self.pep8 and (block_number in self.pep8.pep8checks):
            highlight_errors = self.__highlight_pep8

        char_format = block.charFormat()
        char_format = highlight_errors(char_format, block)
        self.setFormat(0, len(block.text()), char_format)

        for expression, nth, char_format in self.rules:
            index = expression.indexIn(text, 0)

            while index >= 0:
                # We actually want the index of the nth match
                index = expression.pos(nth)
                length = expression.cap(nth).length()
                char_format = highlight_errors(char_format, block)

                if (self.format(index) != STYLES['string']):
                    self.setFormat(index, length, char_format)
                    if char_format == STYLES['string']:
                        hls.append((index, index + length))
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(text, *self.tri_single,
                hls=hls, highlight_errors=highlight_errors)
            if not in_multiline:
                in_multiline = self.match_multiline(text, *self.tri_double,
                    hls=hls, highlight_errors=highlight_errors)
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        #Highlight selected word
        if self.selected_word_pattern is not None:
            index = self.selected_word_pattern.indexIn(text, 0)

            while index >= 0:
                index = self.selected_word_pattern.pos(0)
                length = self.selected_word_pattern.cap(0).length()
                char_format = self.format(index)
                color = QColor()
                color.setNamedColor(STYLES['selectedWord'])
                color.setAlpha(100)
                char_format.setBackground(color)
                self.setFormat(index, length, char_format)
                index = self.selected_word_pattern.indexIn(
                    text, index + length)

        #Spaces
        expression = QRegExp('\s+')
        index = expression.indexIn(text, 0)
        while index >= 0:
            index = expression.pos(0)
            length = expression.cap(0).length()
            char_format = STYLES['spaces']
            if settings.HIGHLIGHT_WHOLE_LINE:
                char_format = highlight_errors(char_format, block)
            self.setFormat(index, length, char_format)
            index = expression.indexIn(text, index + length)
コード例 #45
0
ファイル: connector.py プロジェクト: giserfly/QGIS
        if not self.has_spatial:
            return

        try:
            c = self._execute(None, "SELECT srtext FROM spatial_ref_sys WHERE srid = '%d'" % srid)
        except DbError, e:
            return
        sr = self._fetchone(c)
        self._close_cursor(c)
        if sr is None:
            return

        srtext = sr[0]
        # try to extract just SR name (should be quoted in double quotes)
        regex = QRegExp('"([^"]+)"')
        if regex.indexIn(srtext) > -1:
            srtext = regex.cap(1)
        return srtext


    def isVectorTable(self, table):
        if self.has_geometry_columns and self.has_geometry_columns_access:
            schema, tablename = self.getSchemaTableName(table)
            sql = u"SELECT count(*) FROM geometry_columns WHERE f_table_schema = %s AND f_table_name = %s" % (
                self.quoteString(schema), self.quoteString(tablename))

            c = self._execute(None, sql)
            res = self._fetchone(c)
            self._close_cursor(c)
            return res is not None and res[0] > 0
コード例 #46
0
class XMLHighlighter(QSyntaxHighlighter):
    '''
    Class for highlighting xml text inherited from QSyntaxHighlighter

    reference:
        http://www.yasinuludag.com/blog/?p=49    
    
    '''
    def __init__(self, parent=None):

        super(XMLHighlighter, self).__init__(parent)

        self.highlightingRules = []

        xmlElementFormat = QTextCharFormat()
        xmlElementFormat.setForeground(QColor("#000070"))  #blue
        self.highlightingRules.append(
            (QRegExp("\\b[A-Za-z0-9_]+(?=[\s/>])"), xmlElementFormat))

        xmlAttributeFormat = QTextCharFormat()
        xmlAttributeFormat.setFontItalic(True)
        xmlAttributeFormat.setForeground(QColor("#177317"))  #green
        self.highlightingRules.append(
            (QRegExp("\\b[A-Za-z0-9_]+(?=\\=)"), xmlAttributeFormat))
        self.highlightingRules.append((QRegExp("="), xmlAttributeFormat))

        self.valueFormat = QTextCharFormat()
        self.valueFormat.setForeground(QColor("#e35e00"))  #orange
        self.valueStartExpression = QRegExp("\"")
        self.valueEndExpression = QRegExp("\"(?=[\s></])")

        singleLineCommentFormat = QTextCharFormat()
        singleLineCommentFormat.setForeground(QColor("#a0a0a4"))  #grey
        self.highlightingRules.append(
            (QRegExp("<!--[^\n]*-->"), singleLineCommentFormat))

        textFormat = QTextCharFormat()
        textFormat.setForeground(QColor("#000000"))  #black
        # (?<=...)  - lookbehind is not supported
        self.highlightingRules.append((QRegExp(">(.+)(?=</)"), textFormat))

        keywordFormat = QTextCharFormat()
        keywordFormat.setForeground(QColor("#000070"))  #blue
        keywordFormat.setFontWeight(QFont.Bold)
        keywordPatterns = ["\\b?xml\\b", "/>", ">", "<", "</"]
        self.highlightingRules += [(QRegExp(pattern), keywordFormat)
                                   for pattern in keywordPatterns]

    #VIRTUAL FUNCTION WE OVERRIDE THAT DOES ALL THE COLLORING
    def highlightBlock(self, text):
        #for every pattern
        for pattern, format in self.highlightingRules:
            #Create a regular expression from the retrieved pattern
            expression = QRegExp(pattern)
            #Check what index that expression occurs at with the ENTIRE text
            index = expression.indexIn(text)
            #While the index is greater than 0
            while index >= 0:
                #Get the length of how long the expression is true, set the format from the start to the length with the text format
                length = expression.matchedLength()
                self.setFormat(index, length, format)
                #Set index to where the expression ends in the text
                index = expression.indexIn(text, index + length)

        #HANDLE QUOTATION MARKS NOW.. WE WANT TO START WITH " AND END WITH ".. A THIRD " SHOULD NOT CAUSE THE WORDS INBETWEEN SECOND AND THIRD TO BE COLORED
        self.setCurrentBlockState(0)
        startIndex = 0
        if self.previousBlockState() != 1:
            startIndex = self.valueStartExpression.indexIn(text)
        while startIndex >= 0:
            endIndex = self.valueEndExpression.indexIn(text, startIndex)
            if endIndex == -1:
                self.setCurrentBlockState(1)
                commentLength = len(text) - startIndex
            else:
                commentLength = endIndex - startIndex + self.valueEndExpression.matchedLength(
                )
            self.setFormat(startIndex, commentLength, self.valueFormat)
            startIndex = self.valueStartExpression.indexIn(
                text, startIndex + commentLength)
コード例 #47
0
ファイル: highlighter.py プロジェクト: papablopo07/pireal
class Highlighter(QSyntaxHighlighter):
    """ Syntax Highlighting

    This class defines rules, a rule consists of a QRegExp pattern and a
    QTextCharFormat instance.
    """

    # Keywords
    KEYWORDS = [
        "select",
        "project",
        "rename",
        "product",
        "njoin",
        "difference",
        "intersect",
        "union",
        "and",
        "or"
    ]

    def __init__(self, editor):
        super(Highlighter, self).__init__(editor)
        # Keywords format
        keyword_format = QTextCharFormat()
        keyword_format.setForeground(Qt.darkBlue)
        keyword_format.setFontWeight(QFont.Bold)

        # Rules
        self._rules = [(QRegExp("\\b" + pattern + "\\b"), keyword_format)
                       for pattern in Highlighter.KEYWORDS]

        # Number format
        number_format = QTextCharFormat()
        number_pattern = QRegExp(r"\b([A-Z0-9]+)(?:[ _-](\d+))?\b")
        number_pattern.setMinimal(True)
        number_format.setForeground(Qt.darkCyan)
        self._rules.append((number_pattern, number_format))

        # String format
        string_format = QTextCharFormat()
        string_pattern = QRegExp("\".*\"|\'.*\'")
        string_pattern.setMinimal(True)
        string_format.setForeground(Qt.darkMagenta)
        self._rules.append((string_pattern, string_format))

        # Comment format
        comment_format = QTextCharFormat()
        comment_pattern = QRegExp("--[^\n]*")
        comment_format.setForeground(Qt.darkGreen)
        self._rules.append((comment_pattern, comment_format))

        # Paren
        self.paren = QRegExp('\(|\)')

    def highlightBlock(self, text):
        """ Reimplementation """

        block_data = TextBlockData()
        # Paren
        index = self.paren.indexIn(text, 0)
        while index >= 0:
            matched_paren = str(self.paren.capturedTexts()[0])
            info = ParenInfo(matched_paren, index)
            block_data.insert_paren_info(info)
            index = self.paren.indexIn(text, index + 1)

        self.setCurrentBlockUserData(block_data)

        for pattern, _format in self._rules:
            expression = QRegExp(pattern)
            index = expression.indexIn(text)
            while index >= 0:
                length = expression.matchedLength()
                self.setFormat(index, length, _format)
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
コード例 #48
0
class XMLHighlighter(QSyntaxHighlighter):

    def __init__(self, parent=None):
        super(XMLHighlighter, self).__init__(parent)

        keyword_format = QTextCharFormat()
        keyword_format.setForeground(Qt.darkMagenta)

        keyword_patterns = ["\\b?xml\\b", "/>", ">", "<"]

        self.highlightingRules = [(QRegExp(pattern), keyword_format)
                                  for pattern in keyword_patterns]

        xml_element_format = QTextCharFormat()
        xml_element_format.setForeground(QColor("#117700"))
        self.highlightingRules.append(
            (QRegExp("\\b[A-Za-z0-9_\-]+(?=[\s/>])"), xml_element_format))

        nominatim_area_format = QTextCharFormat()
        nominatim_area_format.setFontItalic(True)
        nominatim_area_format.setFontWeight(QFont.Bold)
        nominatim_area_format.setForeground(QColor("#FF7C00"))
        self.highlightingRules.append(
            (QRegExp("\{\{[A-Za-z0-9:, ]*\}\}"), nominatim_area_format))

        xml_attribute_format = QTextCharFormat()
        xml_attribute_format.setFontItalic(True)
        xml_attribute_format.setForeground(QColor("#2020D2"))
        self.highlightingRules.append(
            (QRegExp("\\b[A-Za-z0-9_]+(?=\\=)"), xml_attribute_format))

        self.value_format = QTextCharFormat()
        self.value_format.setForeground(Qt.red)

        self.value_start_expression = QRegExp("\"")
        self.value_end_expression = QRegExp("\"(?=[\s></])")

        single_line_comment_format = QTextCharFormat()
        single_line_comment_format.setForeground(Qt.gray)
        self.highlightingRules.append(
            (QRegExp("<!--[^\n]*-->"), single_line_comment_format))

    def highlightBlock(self, text):
        # for every pattern
        for pattern, char_format in self.highlightingRules:

            # Create a regular expression from the retrieved pattern
            expression = QRegExp(pattern)

            # Check what index that expression occurs at with the ENTIRE text
            index = expression.indexIn(text)

            # While the index is greater than 0
            while index >= 0:

                # Get the length of how long the expression is true,
                # set the format from the start to the length with
                # the text format
                length = expression.matchedLength()
                self.setFormat(index, length, char_format)

                # Set index to where the expression ends in the text
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)

        start_index = 0
        if self.previousBlockState() != 1:
            start_index = self.value_start_expression.indexIn(text)

        while start_index >= 0:
            end_index = self.value_end_expression.indexIn(text, start_index)

            if end_index == -1:
                self.setCurrentBlockState(1)
                comment_length = len(text) - start_index
            else:
                comment_length = \
                    end_index - start_index + \
                    self.value_end_expression.matchedLength()

            self.setFormat(start_index, comment_length, self.value_format)

            start_index = self.value_start_expression.indexIn(
                text, start_index + comment_length)
コード例 #49
0
ファイル: highlighter.py プロジェクト: uKev/ninja-ide
class Highlighter(QSyntaxHighlighter):

    # braces
    braces = ['\\(', '\\)', '\\{', '\\}', '\\[', '\\]']

    def __init__(self, document, lang=None, scheme=None,
      errors=None, pep8=None):
        QSyntaxHighlighter.__init__(self, document)
        self.highlight_function = self.realtime_highlight
        self.errors = errors
        self.pep8 = pep8
        self.selected_word_lines = []
        self.visible_limits = (0, 50)
        if lang is not None:
            self.apply_highlight(lang, scheme)

    def sanitize(self, word):
        return word.replace('\\', '\\\\')

    def apply_highlight(self, lang, scheme=None, syntax=None):
        if syntax is None:
            langSyntax = settings.SYNTAX.get(lang, {})
        else:
            langSyntax = syntax
        if scheme is not None:
            restyle(scheme)

        keywords = langSyntax.get('keywords', [])
        operators = langSyntax.get('operators', [])
        extras = langSyntax.get('extras', [])

        rules = []

        # Keyword, operator, brace and extras rules
        keyword_pattern = '(^|[^\w\.]{1})(%s)([^\w]{1}|$)'
        rules += [(keyword_pattern % w, 2, STYLES['keyword'])
            for w in keywords]
        rules += [(r'%s' % o, 0, STYLES['operator'])
            for o in operators]
        rules += [(r'%s' % b, 0, STYLES['brace'])
            for b in Highlighter.braces]
        rules += [(keyword_pattern % e, 2, STYLES['extras'])
            for e in extras]

        # All other rules
        proper = langSyntax.get('properObject', None)
        if proper is not None:
            proper = r'\b%s\b' % str(proper[0])
            rules += [(proper, 0, STYLES['properObject'])]

        rules.append((r'__\w+__', 0, STYLES['properObject']))

        definition = langSyntax.get('definition', [])
        for de in definition:
            expr = r'\b%s\b\s*(\w+)' % de
            rules.append((expr, 1, STYLES['definition']))

        # Numeric literals
        rules += [
            (r'\b[+-]?[0-9]+[lL]?\b', 0, STYLES['numbers']),
            (r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, STYLES['numbers']),
            (r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0,
            STYLES['numbers']),
        ]

        regex = langSyntax.get('regex', [])
        for reg in regex:
            expr = reg[0]
            color = resources.COLOR_SCHEME['extras']
            style = ''
            if len(reg) > 1:
                if reg[1] in resources.CUSTOM_SCHEME:
                    color = resources.CUSTOM_SCHEME[reg[1]]
                elif reg[1] in resources.COLOR_SCHEME:
                    color = resources.COLOR_SCHEME[reg[1]]
            if len(reg) > 2:
                style = reg[2]
            rules.append((expr, 0, format(color, style)))

        stringChar = langSyntax.get('string', [])
        for sc in stringChar:
            expr = r'"[^"\\]*(\\.[^"\\]*)*"' if sc == '"' \
                else r"'[^'\\]*(\\.[^'\\]*)*'"
            rules.append((expr, 0, STYLES['string']))

        comments = langSyntax.get('comment', [])
        for co in comments:
            expr = co + '[^\\n]*'
            rules.append((expr, 0, STYLES['comment']))

        # Multi-line strings (expression, flag, style)
        # FIXME: The triple-quotes in these two lines will mess up the
        # syntax highlighting from this point onward
        self.tri_single = (QRegExp("'''"), 1, STYLES["string2"])
        self.tri_double = (QRegExp('"""'), 2, STYLES['string2'])

        multi = langSyntax.get('multiline_comment', [])
        if multi:
            self.multi_start = (QRegExp(multi['open']), STYLES['comment'])
            self.multi_end = (QRegExp(multi['close']), STYLES['comment'])
        else:
            self.multi_start = None

        # Build a QRegExp for each pattern
        self.rules = [(QRegExp(pat), index, fmt)
            for (pat, index, fmt) in rules]
        self.selected_word_pattern = None
        #Apply Highlight to the document... (when colors change)
        self.rehighlight()

    def set_selected_word(self, word):
        """Set the word to highlight."""
        if len(word) > 2:
            self.selected_word_pattern = QRegExp(
                r'\b%s\b' % self.sanitize(word))
        else:
            self.selected_word_pattern = None

    def __highlight_pep8(self, char_format, block):
        """Highlight the lines with errors."""
        block.setUserData(SyntaxUserData(True))
        char_format = char_format.toCharFormat()
        char_format.setUnderlineColor(QColor(
            resources.CUSTOM_SCHEME.get('pep8-underline',
                resources.COLOR_SCHEME['pep8-underline'])))
        char_format.setUnderlineStyle(
            QTextCharFormat.WaveUnderline)
        return char_format

    def __highlight_lint(self, char_format, block):
        """Highlight the lines with errors."""
        block.setUserData(SyntaxUserData(True))
        char_format = char_format.toCharFormat()
        char_format.setUnderlineColor(QColor(
            resources.CUSTOM_SCHEME.get('error-underline',
                resources.COLOR_SCHEME['error-underline'])))
        char_format.setUnderlineStyle(
            QTextCharFormat.WaveUnderline)
        return char_format

    def __clean_error(self, char_format, block):
        block.setUserData(SyntaxUserData())
        return char_format

    def highlightBlock(self, text):
        """Apply syntax highlighting to the given block of text."""
        self.highlight_function(text)

    def set_open_visible_area(self, is_line, position):
        if is_line:
            self.visible_limits = (position - 50, position + 50)

    def open_highlight(self, text):
        if self.visible_limits[0] <= self.currentBlock().blockNumber() <= \
           self.visible_limits[1]:
            self.realtime_highlight(text)
        else:
            self.setCurrentBlockState(0)

    def async_highlight(self):
        self.thread_highlight = HighlightParserThread(self)
        self.connect(self.thread_highlight, SIGNAL("finished()"),
            self._execute_threaded_highlight)
        self.thread_highlight.start()

    def _execute_threaded_highlight(self):
        self.highlight_function = self.threaded_highlight
        if self.thread_highlight and self.thread_highlight.styles:
            lines = list(set(self.thread_highlight.styles.keys()) -
                set(range(self.visible_limits[0], self.visible_limits[1])))
            self.rehighlight_lines(lines, False)
            self.thread_highlight = None
        self.highlight_function = self.realtime_highlight

    def threaded_highlight(self, text):
        hls = []
        block = self.currentBlock()
        block_number = block.blockNumber()
        highlight_errors = self.__clean_error
        if self.errors and (block_number in self.errors.errorsSummary):
            highlight_errors = self.__highlight_lint
        elif self.pep8 and (block_number in self.pep8.pep8checks):
            highlight_errors = self.__highlight_pep8

        char_format = block.charFormat()
        char_format = highlight_errors(char_format, block)
        self.setFormat(0, len(block.text()), char_format)

        styles = self.thread_highlight.styles.get(block.blockNumber(), ())
        for index, length, char_format in styles:
            char_format = highlight_errors(char_format, block)
            if (self.format(index) != STYLES['string']):
                self.setFormat(index, length, char_format)
                if char_format == STYLES['string']:
                    hls.append((index, index + length))

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(text, *self.tri_single,
                hls=hls, highlight_errors=highlight_errors)
            if not in_multiline:
                in_multiline = self.match_multiline(text, *self.tri_double,
                    hls=hls, highlight_errors=highlight_errors)
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

    def realtime_highlight(self, text):
        hls = []
        block = self.currentBlock()
        block_number = block.blockNumber()
        highlight_errors = self.__clean_error
        if self.errors and (block_number in self.errors.errorsSummary):
            highlight_errors = self.__highlight_lint
        elif self.pep8 and (block_number in self.pep8.pep8checks):
            highlight_errors = self.__highlight_pep8

        char_format = block.charFormat()
        char_format = highlight_errors(char_format, block)
        self.setFormat(0, len(block.text()), char_format)

        for expression, nth, char_format in self.rules:
            index = expression.indexIn(text, 0)

            while index >= 0:
                # We actually want the index of the nth match
                index = expression.pos(nth)
                length = expression.cap(nth).length()
                char_format = highlight_errors(char_format, block)

                if (self.format(index) != STYLES['string']):
                    self.setFormat(index, length, char_format)
                    if char_format == STYLES['string']:
                        hls.append((index, index + length))
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(text, *self.tri_single,
                hls=hls, highlight_errors=highlight_errors)
            if not in_multiline:
                in_multiline = self.match_multiline(text, *self.tri_double,
                    hls=hls, highlight_errors=highlight_errors)
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        #Highlight selected word
        if self.selected_word_pattern is not None:
            index = self.selected_word_pattern.indexIn(text, 0)

            while index >= 0:
                index = self.selected_word_pattern.pos(0)
                length = self.selected_word_pattern.cap(0).length()
                char_format = self.format(index)
                color = QColor()
                color.setNamedColor(STYLES['selectedWord'])
                color.setAlpha(100)
                char_format.setBackground(color)
                self.setFormat(index, length, char_format)
                index = self.selected_word_pattern.indexIn(
                    text, index + length)

        #Spaces
        expression = QRegExp('\s+')
        index = expression.indexIn(text, 0)
        while index >= 0:
            index = expression.pos(0)
            length = expression.cap(0).length()
            char_format = STYLES['spaces']
            if settings.HIGHLIGHT_WHOLE_LINE:
                char_format = highlight_errors(char_format, block)
            self.setFormat(index, length, char_format)
            index = expression.indexIn(text, index + length)

    def _rehighlight_lines(self, lines):
        for line in lines:
            block = self.document().findBlockByNumber(line)
            self.rehighlightBlock(block)

    def _get_errors_lines(self):
        errors_lines = []
        block = self.document().begin()
        while block.isValid():
            user_data = block.userData()
            if (user_data is not None) and (user_data.error == True):
                errors_lines.append(block.blockNumber())
            block = block.next()
        return errors_lines

    def rehighlight_lines(self, lines, errors=True):
        if errors:
            errors_lines = self._get_errors_lines()
            refresh_lines = set(lines + errors_lines)
        else:
            refresh_lines = set(lines + self.selected_word_lines)
            self.selected_word_lines = lines
        self._rehighlight_lines(refresh_lines)

    def match_multiline(self, text, delimiter, in_state, style,
        hls=[], highlight_errors=lambda x: x):
        """Do highlighting of multi-line strings. ``delimiter`` should be a
        ``QRegExp`` for triple-single-quotes or triple-double-quotes, and
        ``in_state`` should be a unique integer to represent the corresponding
        state changes when inside those strings. Returns True if we're still
        inside a multi-line string when this function is finished.
        """
        # If inside triple-single quotes, start at 0
        if self.previousBlockState() == in_state:
            start = 0
            add = 0
        # Otherwise, look for the delimiter on this line
        else:
            start = delimiter.indexIn(text)
            # Move past this match
            add = delimiter.matchedLength()

        # As long as there's a delimiter match on this line...
        while start >= 0:
            # Look for the ending delimiter
            end = delimiter.indexIn(text, start + add)
            # Ending delimiter on this line?
            if end >= add:
                length = end - start + add + delimiter.matchedLength()
                self.setCurrentBlockState(0)
            # No; multi-line string
            else:
                self.setCurrentBlockState(in_state)
                length = text.length() - start + add

            st_fmt = self.format(start)
            start_collides = [pos for pos in hls if pos[0] < start < pos[1]]

            # Apply formatting
            if ((st_fmt != STYLES['comment']) or \
               ((st_fmt == STYLES['comment']) and
               (self.previousBlockState() != 0))) and \
                (len(start_collides) == 0):
                style = highlight_errors(style, self.currentBlock())
                self.setFormat(start, length, style)
            else:
                self.setCurrentBlockState(0)
            # Look for the next match
            start = delimiter.indexIn(text, start + length)

        # Return True if still inside a multi-line string, False otherwise
        if self.currentBlockState() == in_state:
            return True
        else:
            return False

    def comment_multiline(self, text, delimiter_end, delimiter_start, style):
        startIndex = 0
        if self.previousBlockState() != 1:
            startIndex = delimiter_start.indexIn(text)
        while startIndex >= 0:
            endIndex = delimiter_end.indexIn(text, startIndex)
            commentLength = 0
            if endIndex == -1:
                self.setCurrentBlockState(1)
                commentLength = text.length() - startIndex
            else:
                commentLength = endIndex - startIndex + \
                    delimiter_end.matchedLength()

            self.setFormat(startIndex, commentLength, style)
            startIndex = delimiter_start.indexIn(text,
                startIndex + commentLength)
コード例 #50
0
ファイル: highlighter.py プロジェクト: daqing15/ninja-ide
    def realtime_highlight(self, text):
        """Highlight each line while it is being edited.

        This function apply the proper highlight to the line being edited
        by the user, this is a really fast process for each line once you
        already have the document highlighted, but slow to do it the first
        time to highlight all the lines together."""
        hls = []
        block = self.currentBlock()
        user_data = block.userData()
        if user_data is None:
            user_data = SyntaxUserData(False)
        user_data.clear_data()
        block_number = block.blockNumber()
        highlight_errors = lambda cf, ud: cf
        if self.errors and (block_number in self.errors.errorsSummary):
            highlight_errors = self.__highlight_lint
        elif self.pep8 and (block_number in self.pep8.pep8checks):
            highlight_errors = self.__highlight_pep8
        elif self.migration and (block_number in self.migration.migration_data):
            highlight_errors = self.__highlight_migration

        char_format = block.charFormat()
        char_format = highlight_errors(char_format, user_data)
        self.setFormat(0, len(block.text()), char_format)

        for expression, nth, char_format in self.rules:
            index = expression.indexIn(text, 0)

            while index >= 0:
                # We actually want the index of the nth match
                index = expression.pos(nth)
                length = len(expression.cap(nth))
                char_format = highlight_errors(char_format, user_data)

                if self.format(index) != STYLES["string"]:
                    self.setFormat(index, length, char_format)
                    if char_format == STYLES["string"]:
                        hls.append((index, index + length))
                        user_data.add_str_group(index, index + length)
                    elif char_format == STYLES["comment"]:
                        user_data.comment_start_at(index)
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(
                text, *self.tri_single, hls=hls, highlight_errors=highlight_errors, user_data=user_data
            )
            if not in_multiline:
                in_multiline = self.match_multiline(
                    text, *self.tri_double, hls=hls, highlight_errors=highlight_errors, user_data=user_data
                )
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        # Highlight selected word
        if self.selected_word_pattern is not None:
            index = self.selected_word_pattern.indexIn(text, 0)

            while index >= 0:
                index = self.selected_word_pattern.pos(0)
                length = len(self.selected_word_pattern.cap(0))
                char_format = self.format(index)
                color = STYLES["selectedWord"].foreground().color()
                color.setAlpha(100)
                char_format.setBackground(color)
                self.setFormat(index, length, char_format)
                index = self.selected_word_pattern.indexIn(text, index + length)

        # Spaces
        expression = QRegExp("\s+")
        index = expression.indexIn(text, 0)
        while index >= 0:
            index = expression.pos(0)
            length = len(expression.cap(0))
            char_format = STYLES["spaces"]
            char_format = highlight_errors(char_format, user_data)
            self.setFormat(index, length, char_format)
            index = expression.indexIn(text, index + length)

        block.setUserData(user_data)
コード例 #51
0
ファイル: highlighter.py プロジェクト: aguzubiaga/ninja-ide
class Highlighter(QSyntaxHighlighter):

    # braces
    braces = ['\\(', '\\)', '\\{', '\\}', '\\[', '\\]']

    def __init__(self, document, lang=None, scheme=None,
      errors=None, pep8=None):
        QSyntaxHighlighter.__init__(self, document)
        self.highlight_function = self.realtime_highlight
        self.errors = errors
        self.pep8 = pep8
        self.selected_word_lines = []
        self.visible_limits = (0, 50)
        self._styles = {}
        if lang is not None:
            self.apply_highlight(lang, scheme)

    def sanitize(self, word):
        return word.replace('\\', '\\\\')

    def apply_highlight(self, lang, scheme=None, syntax=None):
        if syntax is None:
            langSyntax = settings.SYNTAX.get(lang, {})
        else:
            langSyntax = syntax
        if scheme is not None:
            restyle(scheme)

        keywords = langSyntax.get('keywords', [])
        operators = langSyntax.get('operators', [])
        extras = langSyntax.get('extras', [])

        rules = []

        # Keyword, operator, brace and extras rules
        keyword_pattern = '(^|[^\w\.]{1})(%s)([^\w]{1}|$)'
        rules += [(keyword_pattern % w, 2, STYLES['keyword'])
            for w in keywords]
        rules += [(r'%s' % o, 0, STYLES['operator'])
            for o in operators]
        rules += [(r'%s' % b, 0, STYLES['brace'])
            for b in Highlighter.braces]
        rules += [(keyword_pattern % e, 2, STYLES['extras'])
            for e in extras]

        # All other rules
        proper = langSyntax.get('properObject', None)
        if proper is not None:
            proper = r'\b%s\b' % str(proper[0])
            rules += [(proper, 0, STYLES['properObject'])]

        rules.append((r'__\w+__', 0, STYLES['properObject']))

        definition = langSyntax.get('definition', [])
        for de in definition:
            expr = r'\b%s\b\s*(\w+)' % de
            rules.append((expr, 1, STYLES['definition']))

        # Numeric literals
        rules += [
            (r'\b[+-]?[0-9]+[lL]?\b', 0, STYLES['numbers']),
            (r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, STYLES['numbers']),
            (r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0,
            STYLES['numbers']),
        ]

        regex = langSyntax.get('regex', [])
        for reg in regex:
            expr = reg[0]
            color = resources.COLOR_SCHEME['extras']
            style = ''
            if len(reg) > 1:
                if reg[1] in resources.CUSTOM_SCHEME:
                    color = resources.CUSTOM_SCHEME[reg[1]]
                elif reg[1] in resources.COLOR_SCHEME:
                    color = resources.COLOR_SCHEME[reg[1]]
            if len(reg) > 2:
                style = reg[2]
            rules.append((expr, 0, format(color, style)))

        stringChar = langSyntax.get('string', [])
        for sc in stringChar:
            expr = r'"[^"\\]*(\\.[^"\\]*)*"' if sc == '"' \
                else r"'[^'\\]*(\\.[^'\\]*)*'"
            rules.append((expr, 0, STYLES['string']))

        comments = langSyntax.get('comment', [])
        for co in comments:
            expr = co + '[^\\n]*'
            rules.append((expr, 0, STYLES['comment']))

        # Multi-line strings (expression, flag, style)
        # FIXME: The triple-quotes in these two lines will mess up the
        # syntax highlighting from this point onward
        self.tri_single = (QRegExp("'''"), 1, STYLES["string2"])
        self.tri_double = (QRegExp('"""'), 2, STYLES['string2'])

        multi = langSyntax.get('multiline_comment', [])
        if multi:
            self.multi_start = (QRegExp(multi['open']), STYLES['comment'])
            self.multi_end = (QRegExp(multi['close']), STYLES['comment'])
        else:
            self.multi_start = None

        # Build a QRegExp for each pattern
        self.rules = [(QRegExp(pat), index, fmt)
            for (pat, index, fmt) in rules]
        self.selected_word_pattern = None
        #Apply Highlight to the document... (when colors change)
        self.rehighlight()

    def set_selected_word(self, word):
        """Set the word to highlight."""
        if len(word) > 2:
            self.selected_word_pattern = QRegExp(
                r'\b%s\b' % self.sanitize(word))
        else:
            self.selected_word_pattern = None

    def __highlight_pep8(self, char_format, user_data):
        """Highlight the lines with errors."""
        user_data.error = True
        char_format = char_format.toCharFormat()
        char_format.setUnderlineColor(QColor(
            resources.CUSTOM_SCHEME.get('pep8-underline',
                resources.COLOR_SCHEME['pep8-underline'])))
        char_format.setUnderlineStyle(
            QTextCharFormat.WaveUnderline)
        return char_format

    def __highlight_lint(self, char_format, user_data):
        """Highlight the lines with errors."""
        user_data.error = True
        char_format = char_format.toCharFormat()
        char_format.setUnderlineColor(QColor(
            resources.CUSTOM_SCHEME.get('error-underline',
                resources.COLOR_SCHEME['error-underline'])))
        char_format.setUnderlineStyle(
            QTextCharFormat.WaveUnderline)
        return char_format

    def highlightBlock(self, text):
        """Apply syntax highlighting to the given block of text."""
        self.highlight_function(text)

    def set_open_visible_area(self, is_line, position):
        if is_line:
            self.visible_limits = (position - 50, position + 50)

    def open_highlight(self, text):
        if self.visible_limits[0] <= self.currentBlock().blockNumber() <= \
           self.visible_limits[1]:
            self.realtime_highlight(text)
        else:
            self.setCurrentBlockState(0)

    def async_highlight(self):
        self.thread_highlight = HighlightParserThread(self)
        self.connect(self.thread_highlight,
            SIGNAL("highlightingDetected(PyQt_PyObject)"),
            self._execute_threaded_highlight)
        self.thread_highlight.start()

    def _execute_threaded_highlight(self, styles=None):
        self.highlight_function = self.threaded_highlight
        if styles:
            self._styles = styles
            lines = list(set(styles.keys()) -
                set(range(self.visible_limits[0], self.visible_limits[1])))
            self.rehighlight_lines(lines, False)
        else:
            self._styles = {}
        self.highlight_function = self.realtime_highlight

    def threaded_highlight(self, text):
        hls = []
        block = self.currentBlock()
        user_data = block.userData()
        if user_data is None:
            user_data = SyntaxUserData(False)
        user_data.clear_data()
        block_number = block.blockNumber()
        highlight_errors = lambda cf, ud: cf
        if self.errors and (block_number in self.errors.errorsSummary):
            highlight_errors = self.__highlight_lint
        elif self.pep8 and (block_number in self.pep8.pep8checks):
            highlight_errors = self.__highlight_pep8

        char_format = block.charFormat()
        char_format = highlight_errors(char_format, user_data)
        self.setFormat(0, len(block.text()), char_format)

        block_styles = self._styles.get(block.blockNumber(), ())
        for index, length, char_format in block_styles:
            char_format = highlight_errors(char_format, user_data)
            if (self.format(index) != STYLES['string']):
                self.setFormat(index, length, char_format)
                if char_format == STYLES['string']:
                    hls.append((index, index + length))
                    user_data.add_str_group(index, index + length)
                elif char_format == STYLES['comment']:
                    user_data.comment_start_at(index)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(text, *self.tri_single,
                hls=hls, highlight_errors=highlight_errors,
                user_data=user_data)
            if not in_multiline:
                in_multiline = self.match_multiline(text, *self.tri_double,
                    hls=hls, highlight_errors=highlight_errors,
                    user_data=user_data)
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        block.setUserData(user_data)

    def realtime_highlight(self, text):
        hls = []
        block = self.currentBlock()
        user_data = block.userData()
        if user_data is None:
            user_data = SyntaxUserData(False)
        user_data.clear_data()
        block_number = block.blockNumber()
        highlight_errors = lambda cf, ud: cf
        if self.errors and (block_number in self.errors.errorsSummary):
            highlight_errors = self.__highlight_lint
        elif self.pep8 and (block_number in self.pep8.pep8checks):
            highlight_errors = self.__highlight_pep8

        char_format = block.charFormat()
        char_format = highlight_errors(char_format, user_data)
        self.setFormat(0, len(block.text()), char_format)

        for expression, nth, char_format in self.rules:
            index = expression.indexIn(text, 0)

            while index >= 0:
                # We actually want the index of the nth match
                index = expression.pos(nth)
                length = expression.cap(nth).length()
                char_format = highlight_errors(char_format, user_data)

                if (self.format(index) != STYLES['string']):
                    self.setFormat(index, length, char_format)
                    if char_format == STYLES['string']:
                        hls.append((index, index + length))
                        user_data.add_str_group(index, index + length)
                    elif char_format == STYLES['comment']:
                        user_data.comment_start_at(index)
                index = expression.indexIn(text, index + length)

        self.setCurrentBlockState(0)
        if not self.multi_start:
            # Do multi-line strings
            in_multiline = self.match_multiline(text, *self.tri_single,
                hls=hls, highlight_errors=highlight_errors,
                user_data=user_data)
            if not in_multiline:
                in_multiline = self.match_multiline(text, *self.tri_double,
                    hls=hls, highlight_errors=highlight_errors,
                    user_data=user_data)
        else:
            # Do multi-line comment
            self.comment_multiline(text, self.multi_end[0], *self.multi_start)

        #Highlight selected word
        if self.selected_word_pattern is not None:
            index = self.selected_word_pattern.indexIn(text, 0)

            while index >= 0:
                index = self.selected_word_pattern.pos(0)
                length = self.selected_word_pattern.cap(0).length()
                char_format = self.format(index)
                color = QColor()
                color.setNamedColor(STYLES['selectedWord'])
                color.setAlpha(100)
                char_format.setBackground(color)
                self.setFormat(index, length, char_format)
                index = self.selected_word_pattern.indexIn(
                    text, index + length)

        #Spaces
        expression = QRegExp('\s+')
        index = expression.indexIn(text, 0)
        while index >= 0:
            index = expression.pos(0)
            length = expression.cap(0).length()
            char_format = STYLES['spaces']
            if settings.HIGHLIGHT_WHOLE_LINE:
                char_format = highlight_errors(char_format, user_data)
            self.setFormat(index, length, char_format)
            index = expression.indexIn(text, index + length)

        block.setUserData(user_data)

    def _rehighlight_lines(self, lines):
        if self.document() is None:
            return
        for line in lines:
            block = self.document().findBlockByNumber(line)
            self.rehighlightBlock(block)

    def _get_errors_lines(self):
        errors_lines = []
        block = self.document().begin()
        while block.isValid():
            user_data = block.userData()
            if (user_data is not None) and (user_data.error == True):
                errors_lines.append(block.blockNumber())
            block = block.next()
        return errors_lines

    def rehighlight_lines(self, lines, errors=True):
        if errors:
            errors_lines = self._get_errors_lines()
            refresh_lines = set(lines + errors_lines)
        else:
            refresh_lines = set(lines + self.selected_word_lines)
            self.selected_word_lines = lines
        self._rehighlight_lines(refresh_lines)

    def match_multiline(self, text, delimiter, in_state, style,
        hls=[], highlight_errors=lambda x: x, user_data=None):
        """Do highlighting of multi-line strings. ``delimiter`` should be a
        ``QRegExp`` for triple-single-quotes or triple-double-quotes, and
        ``in_state`` should be a unique integer to represent the corresponding
        state changes when inside those strings. Returns True if we're still
        inside a multi-line string when this function is finished.
        """
        # If inside triple-single quotes, start at 0
        if self.previousBlockState() == in_state:
            start = 0
            add = 0
        # Otherwise, look for the delimiter on this line
        else:
            start = delimiter.indexIn(text)
            # Move past this match
            add = delimiter.matchedLength()

        # As long as there's a delimiter match on this line...
        while start >= 0:
            # Look for the ending delimiter
            end = delimiter.indexIn(text, start + add)
            # Ending delimiter on this line?
            if end >= add:
                length = end - start + add + delimiter.matchedLength()
                self.setCurrentBlockState(0)
            # No; multi-line string
            else:
                self.setCurrentBlockState(in_state)
                length = text.length() - start + add

            st_fmt = self.format(start)
            start_collides = [pos for pos in hls if pos[0] < start < pos[1]]

            # Apply formatting
            if ((st_fmt != STYLES['comment']) or \
               ((st_fmt == STYLES['comment']) and
               (self.previousBlockState() != 0))) and \
                (len(start_collides) == 0):
                if user_data is not None:
                    style = highlight_errors(style, user_data)
                self.setFormat(start, length, style)
            else:
                self.setCurrentBlockState(0)
            # Look for the next match
            start = delimiter.indexIn(text, start + length)

        # Return True if still inside a multi-line string, False otherwise
        if self.currentBlockState() == in_state:
            return True
        else:
            return False

    def comment_multiline(self, text, delimiter_end, delimiter_start, style):
        startIndex = 0
        if self.previousBlockState() != 1:
            startIndex = delimiter_start.indexIn(text)
        while startIndex >= 0:
            endIndex = delimiter_end.indexIn(text, startIndex)
            commentLength = 0
            if endIndex == -1:
                self.setCurrentBlockState(1)
                commentLength = text.length() - startIndex
            else:
                commentLength = endIndex - startIndex + \
                    delimiter_end.matchedLength()

            self.setFormat(startIndex, commentLength, style)
            startIndex = delimiter_start.indexIn(text,
                startIndex + commentLength)