1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
# -*- coding: utf-8 -*-
"""
Contains the default indenter.
"""
import logging
from pyqode.core.api import TextHelper
from pyqode.core.api.mode import Mode
from pyqode.qt import QtGui
def _logger():
return logging.getLogger(__name__)
def debug(msg, *args):
return _logger().log(5, msg, *args)
class IndenterMode(Mode):
""" Implements classic indentation/tabulation (Tab/Shift+Tab)
It inserts/removes tabulations (a series of spaces defined by the
tabLength settings) at the cursor position if there is no selection,
otherwise it fully indents/un-indents selected lines.
To trigger an indentation/un-indentation programatically, you must emit
:attr:`pyqode.core.api.CodeEdit.indent_requested` or
:attr:`pyqode.core.api.CodeEdit.unindent_requested`.
"""
def __init__(self):
super(IndenterMode, self).__init__()
def on_state_changed(self, state):
if state:
self.editor.indent_requested.connect(self.indent)
self.editor.unindent_requested.connect(self.unindent)
else:
self.editor.indent_requested.disconnect(self.indent)
self.editor.unindent_requested.disconnect(self.unindent)
def indent_selection(self, cursor):
"""
Indent selected text
:param cursor: QTextCursor
"""
doc = self.editor.document()
tab_len = self.editor.tab_length
cursor.beginEditBlock()
nb_lines = len(cursor.selection().toPlainText().splitlines())
c = self.editor.textCursor()
if c.atBlockStart() and c.position() == c.selectionEnd():
nb_lines += 1
block = doc.findBlock(cursor.selectionStart())
i = 0
# indent every lines
while i < nb_lines:
nb_space_to_add = tab_len
cursor = QtGui.QTextCursor(block)
cursor.movePosition(cursor.StartOfLine, cursor.MoveAnchor)
if self.editor.use_spaces_instead_of_tabs:
for _ in range(nb_space_to_add):
cursor.insertText(" ")
else:
cursor.insertText('\t')
block = block.next()
i += 1
cursor.endEditBlock()
def unindent_selection(self, cursor):
"""
Un-indents selected text
:param cursor: QTextCursor
"""
doc = self.editor.document()
tab_len = self.editor.tab_length
nb_lines = len(cursor.selection().toPlainText().splitlines())
if nb_lines == 0:
nb_lines = 1
block = doc.findBlock(cursor.selectionStart())
assert isinstance(block, QtGui.QTextBlock)
i = 0
debug('unindent selection: %d lines', nb_lines)
while i < nb_lines:
txt = block.text()
debug('line to unindent: %s', txt)
debug('self.editor.use_spaces_instead_of_tabs: %r',
self.editor.use_spaces_instead_of_tabs)
if self.editor.use_spaces_instead_of_tabs:
indentation = (len(txt) - len(txt.lstrip()))
else:
indentation = len(txt) - len(txt.replace('\t', ''))
debug('unindent line %d: %d spaces', i, indentation)
if indentation > 0:
c = QtGui.QTextCursor(block)
c.movePosition(c.StartOfLine, cursor.MoveAnchor)
for _ in range(tab_len):
txt = block.text()
if len(txt) and txt[0] == ' ':
c.deleteChar()
block = block.next()
i += 1
return cursor
def indent(self):
"""
Indents text at cursor position.
"""
cursor = self.editor.textCursor()
assert isinstance(cursor, QtGui.QTextCursor)
if cursor.hasSelection():
self.indent_selection(cursor)
else:
# simply insert indentation at the cursor position
tab_len = self.editor.tab_length
cursor.beginEditBlock()
if self.editor.use_spaces_instead_of_tabs:
nb_space_to_add = tab_len - cursor.positionInBlock() % tab_len
cursor.insertText(nb_space_to_add * " ")
else:
cursor.insertText('\t')
cursor.endEditBlock()
def count_deletable_spaces(self, cursor, max_spaces):
# count the number of spaces deletable, stop at tab len
max_spaces = abs(max_spaces)
if max_spaces > self.editor.tab_length:
max_spaces = self.editor.tab_length
spaces = 0
trav_cursor = QtGui.QTextCursor(cursor)
while spaces < max_spaces or trav_cursor.atBlockStart():
pos = trav_cursor.position()
trav_cursor.movePosition(cursor.Left, cursor.KeepAnchor)
char = trav_cursor.selectedText()
if char == " ":
spaces += 1
else:
break
trav_cursor.setPosition(pos - 1)
return spaces
def unindent(self):
"""
Un-indents text at cursor position.
"""
debug('unindent')
cursor = self.editor.textCursor()
debug('cursor has selection %r', cursor.hasSelection())
if cursor.hasSelection():
cursor.beginEditBlock()
self.unindent_selection(cursor)
cursor.endEditBlock()
self.editor.setTextCursor(cursor)
else:
tab_len = self.editor.tab_length
indentation = cursor.positionInBlock()
max_spaces = tab_len - (indentation - (indentation % tab_len))
spaces = self.count_deletable_spaces(cursor, max_spaces)
debug('deleting %d space before cursor' % spaces)
cursor.beginEditBlock()
if spaces:
# delete spaces before cursor
for _ in range(spaces):
cursor.deletePreviousChar()
else:
# un-indent whole line
debug('un-indent whole line')
cursor = self.unindent_selection(cursor)
cursor.endEditBlock()
self.editor.setTextCursor(cursor)
debug(cursor.block().text())
|