import re
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.Qsci import *
from QsciScintillaCompat import *
class IgeEditor(QsciScintillaCompat):
""" Class representin featured editor based on Qscintilla"""
def __init__(self, parent):
super().__init__(parent)
self.setAutoIndent(True)
self.setEolMode(QsciScintilla.EolUnix)
#autocompletion
self.Completion = False
#completion of braces
self.BracesCompletion = True
self.connect(self, SIGNAL("SCN_CHARADDED(int)"), self.__charAdded)
######### shortcuts #########
sh_ctrl_sp = QShortcut(QKeySequence('Ctrl+Space'), self)
sh_ctrl_sp.connect(sh_ctrl_sp, SIGNAL('activated()'), self.autocomplete)
# highlite matched braces
self.setBraceMatching(QsciScintilla.StrictBraceMatch)
self.setMatchedBraceBackgroundColor(Qt.yellow)
def mouseMoveEvent(self, event):
""" Emitting MouseMove signal"""
self.emit(SIGNAL("MouseMove"), event)
QsciScintilla.mouseMoveEvent(self, event)
def mouseDoubleClickEvent(self, event):
""" Emitting MouseDoubleClick signal """
self.emit(SIGNAL('MouseDoubleClick'), event)
QsciScintilla.mouseDoubleClickEvent(self, event)
def HighliteWord(self, pos, len):
""" Highlite word
@param pos - start from position pos
@param len - highlite len simbols
!Function clears all other styling and needs to be rewritten!
"""
self.startStyling(pos, 0xff)
self.setStyling(len, 14)
self.startStyling(pos+len, 0)
self.setStyling(100000, 14)
@pyqtSlot()
def autocomplete(self):
self.autoCompleteFromAPIs()
def keyPressEvent(self, event):
#print(event.key())
if event.key() == 16777218:
self.unindentLineOrSelection()
return
needComplete = self.isListActive()
QsciScintilla.keyPressEvent(self, event)
# continue autocompletion if backspase was pressed
if needComplete and event.key() == 16777219:
self.autocomplete()
#autocompletion
bad_keys = {
16777220,
16777216,
16777217,
16777219,
16777234,
16777235,
16777236,
16777237,
16777239,
16777238,
16777232,
16777233,
16777249,
16777248,
16777251,
16777222,
16777223,
16777222,
16777252,
16777248,
16777249,
16777251
}
if self.Completion and event.key() not in bad_keys:
self.autocomplete()
return
def __charAdded(self, char):
#print(char)
line, col = self.getCursorPosition()
if self.BracesCompletion:
char2 = 0
if char in {34, 39}:
char2 = char
if char == 40:
char2 = 41
if char in {91, 123}:
char2 = char + 2
if char2 != 0:
self.insertAt(chr(char2), line, col)
if self.isListActive():
self.autocomplete()
def getWordBoundaries(self, line, index, useWordChars = True):
"""
Public method to get the word boundaries at a position.
@param line number of line to look at (int)
@param index position to look at (int)
@keyparam useWordChars flag indicating to use the wordCharacters
method (boolean)
@return tuple with start and end indices of the word at the position
(integer, integer)
"""
text = self.text(line)
if self.caseSensitive():
cs = Qt.CaseSensitive
else:
cs = Qt.CaseInsensitive
wc = self.wordCharacters()
if wc is None or not useWordChars:
regExp = QRegExp('[^\w_]', cs)
else:
regExp = QRegExp('[^%s]' % re.escape(wc), cs)
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)
return (start, end)
def getWord(self, line, index, direction = 0, useWordChars = True):
"""
Public method to get the word at a position.
@param line number of line to look at (int)
@param index position to look at (int)
@param direction direction to look in (0 = whole word, 1 = left, 2 = right)
@keyparam useWordChars flag indicating to use the wordCharacters
method (boolean)
@return the word at that position (string)
"""
start, end = self.getWordBoundaries(line, index, useWordChars)
if direction == 1:
end = index
elif direction == 2:
start = index
if end > start:
text = self.text(line)
word = text[start:end]
else:
word = ''
return word
def PosToLineIndex(self, pos):
return (self.text().count('\n', 0, pos), pos - self.text().rfind('\n', 0, pos)-1)
def getWordLeft(self, line, index):
"""
Public method to get the word to the left of a position.
@param line number of line to look at (int)
@param index position to look at (int)
@return the word to the left of that position (string)
"""
return self.getWord(line, index, 1)
def getWordRight(self, line, index):
"""
Public method to get the word to the right of a position.
@param line number of line to look at (int)
@param index position to look at (int)
@return the word to the right of that position (string)
"""
return self.getWord(line, index, 2)
def getCurrentWord(self):
"""
Public method to get the word at the current position.
@return the word at that current position (string)
"""
line, index = self.getCursorPosition()
return self.getWord(line, index)
def selectWord(self, line, index):
"""
Public method to select the word at a position.
@param line number of line to look at (int)
@param index position to look at (int)
"""
start, end = self.getWordBoundaries(line, index, False)
self.setSelection(line, start, line, end)
def selectCurrentWord(self):
"""
Public method to select the current word.
"""
line, index = self.getCursorPosition()
self.selectWord(line, index)
############################################################################
## Indentation handling methods below
############################################################################
def __indentLine(self, indent = True):
"""
Private method to indent or unindent the current line.
@param indent flag indicating an indent operation (boolean)
If the flag is true, an indent operation is performed.
Otherwise the current line is unindented.
"""
line, index = self.getCursorPosition()
self.beginUndoAction()
if indent:
self.indent(line)
else:
self.unindent(line)
self.endUndoAction()
if indent:
self.setCursorPosition(line, index + self.indentationWidth())
else:
self.setCursorPosition(line, index - self.indentationWidth())
def __indentSelection(self, indent = True):
"""
Private method to indent or unindent the current selection.
@param indent flag indicating an indent operation (boolean)
If the flag is true, an indent operation is performed.
Otherwise the current line is unindented.
"""
if not self.hasSelectedText():
return
# get the selection
lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
if indexTo == 0:
endLine = lineTo - 1
else:
endLine = lineTo
self.beginUndoAction()
# iterate over the lines
for line in range(lineFrom, endLine + 1):
if indent:
self.indent(line)
else:
self.unindent(line)
self.endUndoAction()
if indent:
if indexTo == 0:
self.setSelection(lineFrom, indexFrom + self.indentationWidth(),
lineTo, 0)
else:
self.setSelection(lineFrom, indexFrom + self.indentationWidth(),
lineTo, indexTo + self.indentationWidth())
else:
indexStart = indexFrom - self.indentationWidth()
if indexStart < 0:
indexStart = 0
indexEnd = indexTo - self.indentationWidth()
if indexEnd < 0:
indexEnd = 0
self.setSelection(lineFrom, indexStart, lineTo, indexEnd)
def indentLineOrSelection(self):
"""
Public slot to indent the current line or current selection
"""
if self.hasSelectedText():
self.__indentSelection(True)
else:
self.__indentLine(True)
def unindentLineOrSelection(self):
"""
Public slot to unindent the current line or current selection.
"""
if self.hasSelectedText():
self.__indentSelection(False)
else:
self.__indentLine(False)
def smartIndentLineOrSelection(self):
"""
Public slot to indent current line smartly.
"""
if self.hasSelectedText():
if self.lexer_ and self.lexer_.hasSmartIndent():
self.lexer_.smartIndentSelection(self)
else:
self.__indentSelection(True)
else:
if self.lexer_ and self.lexer_.hasSmartIndent():
self.lexer_.smartIndentLine(self)
else:
self.__indentLine(True)