109 lines
4.4 KiB
Python
109 lines
4.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
""" Contains the AutoCompleteMode """
|
|
import logging
|
|
from pyqode.qt import QtCore, QtGui
|
|
from pyqode.core.api import TextHelper
|
|
from pyqode.core.api.mode import Mode
|
|
|
|
|
|
class AutoCompleteMode(Mode):
|
|
""" Automatically complete quotes and parentheses
|
|
|
|
Generic auto complete mode that automatically completes the following
|
|
symbols:
|
|
|
|
- " -> "
|
|
- ' -> '
|
|
- ( -> )
|
|
- [ -> ]
|
|
- { -> }
|
|
"""
|
|
#: Auto complete mapping, maps input key with completion text.
|
|
MAPPING = {'"': '"', "'": "'", "(": ")", "{": "}", "[": "]"}
|
|
#: The format to use for each symbol in mapping when there is a selection
|
|
SELECTED_QUOTES_FORMATS = {key: '%s%s%s' for key in MAPPING.keys()}
|
|
#: The format to use for each symbol in mapping when there is no selection
|
|
QUOTES_FORMATS = {key: '%s' for key in MAPPING.keys()}
|
|
|
|
def __init__(self):
|
|
super(AutoCompleteMode, self).__init__()
|
|
self.logger = logging.getLogger(__name__)
|
|
self._ignore_post = False
|
|
|
|
def on_state_changed(self, state):
|
|
if state:
|
|
self.editor.post_key_pressed.connect(self._on_post_key_pressed)
|
|
self.editor.key_pressed.connect(self._on_key_pressed)
|
|
else:
|
|
self.editor.post_key_pressed.disconnect(self._on_post_key_pressed)
|
|
self.editor.key_pressed.disconnect(self._on_key_pressed)
|
|
|
|
def _on_post_key_pressed(self, event):
|
|
if not event.isAccepted() and not self._ignore_post:
|
|
txt = event.text()
|
|
trav = self.editor.textCursor()
|
|
assert isinstance(trav, QtGui.QTextCursor)
|
|
trav.movePosition(trav.Left, trav.MoveAnchor, 2)
|
|
literal = TextHelper(self.editor).is_comment_or_string(trav)
|
|
if not literal:
|
|
next_char = TextHelper(self.editor).get_right_character()
|
|
if txt in self.MAPPING:
|
|
to_insert = self.MAPPING[txt]
|
|
if (not next_char or next_char in self.MAPPING.keys() or
|
|
next_char in self.MAPPING.values() or
|
|
next_char.isspace()):
|
|
TextHelper(self.editor).insert_text(
|
|
self.QUOTES_FORMATS[txt] % to_insert)
|
|
self._ignore_post = False
|
|
|
|
def _on_key_pressed(self, event):
|
|
txt = event.text()
|
|
cursor = self.editor.textCursor()
|
|
from pyqode.qt import QtGui
|
|
assert isinstance(cursor, QtGui.QTextCursor)
|
|
if cursor.hasSelection():
|
|
# quoting of selected text
|
|
if event.text() in self.MAPPING.keys():
|
|
first = event.text()
|
|
last = self.MAPPING[event.text()]
|
|
cursor.insertText(
|
|
self.SELECTED_QUOTES_FORMATS[event.text()] % (
|
|
first, cursor.selectedText(), last))
|
|
self.editor.setTextCursor(cursor)
|
|
event.accept()
|
|
else:
|
|
self._ignore_post = True
|
|
return
|
|
next_char = TextHelper(self.editor).get_right_character()
|
|
self.logger.debug('next char: %s', next_char)
|
|
ignore = False
|
|
if event.key() == QtCore.Qt.Key_Backspace:
|
|
# get the character that will get deleted
|
|
tc = self.editor.textCursor()
|
|
pos = tc.position()
|
|
tc.movePosition(tc.Left)
|
|
tc.movePosition(tc.Right, tc.KeepAnchor)
|
|
del_char = tc.selectedText()
|
|
if del_char in self.MAPPING and \
|
|
self.MAPPING[del_char] == next_char:
|
|
tc.beginEditBlock()
|
|
tc.movePosition(tc.Right, tc.KeepAnchor)
|
|
tc.insertText('')
|
|
tc.setPosition(pos - 2)
|
|
tc.endEditBlock()
|
|
self.editor.setTextCursor(tc)
|
|
ignore = True
|
|
elif txt and next_char == txt and next_char in self.MAPPING:
|
|
ignore = True
|
|
elif event.text() == ')' or event.text() == ']' or event.text() == '}':
|
|
# if typing the same symbol twice, the symbol should not be written
|
|
# and the cursor moved just after the char
|
|
# e.g. if you type ) just before ), the cursor will just move after
|
|
# the existing )
|
|
if next_char == event.text():
|
|
ignore = True
|
|
if ignore:
|
|
event.accept()
|
|
TextHelper(self.editor).clear_selection()
|
|
TextHelper(self.editor).move_right()
|