forked from qt-creator/qt-creator
Support camel-case for word-wise movement
This was DevDay's 3rd most requested feature Done-with: Erik Verbruggen
This commit is contained in:
@@ -1077,6 +1077,382 @@ void BaseTextEditor::cleanWhitespace()
|
|||||||
d->m_document->cleanWhitespace(textCursor());
|
d->m_document->cleanWhitespace(textCursor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// could go into QTextCursor...
|
||||||
|
static QTextLine currentTextLine(const QTextCursor &cursor)
|
||||||
|
{
|
||||||
|
const QTextBlock block = cursor.block();
|
||||||
|
if (!block.isValid())
|
||||||
|
return QTextLine();
|
||||||
|
|
||||||
|
const QTextLayout *layout = block.layout();
|
||||||
|
if (!layout)
|
||||||
|
return QTextLine();
|
||||||
|
|
||||||
|
const int relativePos = cursor.position() - block.position();
|
||||||
|
return layout->lineForTextPosition(relativePos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BaseTextEditor::camelCaseLeft(QTextCursor &cursor, QTextCursor::MoveMode mode)
|
||||||
|
{
|
||||||
|
int state = 0;
|
||||||
|
enum Input {
|
||||||
|
Input_U,
|
||||||
|
Input_l,
|
||||||
|
Input_underscore,
|
||||||
|
Input_space,
|
||||||
|
Input_other
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!cursor.movePosition(QTextCursor::Left, mode))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
forever {
|
||||||
|
QChar c = characterAt(cursor.position());
|
||||||
|
Input input = Input_other;
|
||||||
|
if (c.isUpper())
|
||||||
|
input = Input_U;
|
||||||
|
else if (c.isLower() || c.isDigit())
|
||||||
|
input = Input_l;
|
||||||
|
else if (c == QLatin1Char('_'))
|
||||||
|
input = Input_underscore;
|
||||||
|
else if (c.isSpace() && c != QChar::ParagraphSeparator)
|
||||||
|
input = Input_space;
|
||||||
|
else
|
||||||
|
input = Input_other;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case 0:
|
||||||
|
switch (input) {
|
||||||
|
case Input_U:
|
||||||
|
state = 1;
|
||||||
|
break;
|
||||||
|
case Input_l:
|
||||||
|
state = 2;
|
||||||
|
break;
|
||||||
|
case Input_underscore:
|
||||||
|
state = 3;
|
||||||
|
break;
|
||||||
|
case Input_space:
|
||||||
|
state = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cursor.movePosition(QTextCursor::Right, mode);
|
||||||
|
return cursor.movePosition(QTextCursor::WordLeft, mode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
switch (input) {
|
||||||
|
case Input_U:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
switch (input) {
|
||||||
|
case Input_U:
|
||||||
|
return true;
|
||||||
|
case Input_l:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cursor.movePosition(QTextCursor::Right, mode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
switch (input) {
|
||||||
|
case Input_underscore:
|
||||||
|
break;
|
||||||
|
case Input_U:
|
||||||
|
state = 1;
|
||||||
|
break;
|
||||||
|
case Input_l:
|
||||||
|
state = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cursor.movePosition(QTextCursor::Right, mode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
switch (input) {
|
||||||
|
case Input_space:
|
||||||
|
break;
|
||||||
|
case Input_U:
|
||||||
|
state = 1;
|
||||||
|
break;
|
||||||
|
case Input_l:
|
||||||
|
state = 2;
|
||||||
|
break;
|
||||||
|
case Input_underscore:
|
||||||
|
state = 3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cursor.movePosition(QTextCursor::Right, mode);
|
||||||
|
if (cursor.positionInBlock() == 0)
|
||||||
|
return true;
|
||||||
|
return cursor.movePosition(QTextCursor::WordLeft, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cursor.movePosition(QTextCursor::Left, mode))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BaseTextEditor::camelCaseRight(QTextCursor &cursor, QTextCursor::MoveMode mode)
|
||||||
|
{
|
||||||
|
int state = 0;
|
||||||
|
enum Input {
|
||||||
|
Input_U,
|
||||||
|
Input_l,
|
||||||
|
Input_underscore,
|
||||||
|
Input_space,
|
||||||
|
Input_other
|
||||||
|
};
|
||||||
|
|
||||||
|
forever {
|
||||||
|
QChar c = characterAt(cursor.position());
|
||||||
|
Input input = Input_other;
|
||||||
|
if (c.isUpper())
|
||||||
|
input = Input_U;
|
||||||
|
else if (c.isLower() || c.isDigit())
|
||||||
|
input = Input_l;
|
||||||
|
else if (c == QLatin1Char('_'))
|
||||||
|
input = Input_underscore;
|
||||||
|
else if (c.isSpace() && c != QChar::ParagraphSeparator)
|
||||||
|
input = Input_space;
|
||||||
|
else
|
||||||
|
input = Input_other;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case 0:
|
||||||
|
switch (input) {
|
||||||
|
case Input_U:
|
||||||
|
state = 4;
|
||||||
|
break;
|
||||||
|
case Input_l:
|
||||||
|
state = 1;
|
||||||
|
break;
|
||||||
|
case Input_underscore:
|
||||||
|
state = 6;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return cursor.movePosition(QTextCursor::WordRight, mode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
switch (input) {
|
||||||
|
case Input_U:
|
||||||
|
return true;
|
||||||
|
case Input_l:
|
||||||
|
break;
|
||||||
|
case Input_underscore:
|
||||||
|
state = 6;
|
||||||
|
break;
|
||||||
|
case Input_space:
|
||||||
|
state = 7;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
switch (input) {
|
||||||
|
case Input_U:
|
||||||
|
break;
|
||||||
|
case Input_l:
|
||||||
|
cursor.movePosition(QTextCursor::Left, mode);
|
||||||
|
return true;
|
||||||
|
case Input_space:
|
||||||
|
state = 7;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return cursor.movePosition(QTextCursor::WordRight, mode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
switch (input) {
|
||||||
|
case Input_U:
|
||||||
|
state = 2;
|
||||||
|
break;
|
||||||
|
case Input_l:
|
||||||
|
state = 1;
|
||||||
|
break;
|
||||||
|
case Input_underscore:
|
||||||
|
state = 6;
|
||||||
|
break;
|
||||||
|
case Input_space:
|
||||||
|
state = 7;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return cursor.movePosition(QTextCursor::WordRight, mode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
switch (input) {
|
||||||
|
case Input_underscore:
|
||||||
|
break;
|
||||||
|
case Input_space:
|
||||||
|
state = 7;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return cursor.movePosition(QTextCursor::WordRight, mode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
switch (input) {
|
||||||
|
case Input_space:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cursor.movePosition(QTextCursor::Right, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BaseTextEditor::cursorMoveKeyEvent(QKeyEvent *e)
|
||||||
|
{
|
||||||
|
QTextCursor cursor = textCursor();
|
||||||
|
|
||||||
|
QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
|
||||||
|
QTextCursor::MoveOperation op = QTextCursor::NoMove;
|
||||||
|
|
||||||
|
if (e == QKeySequence::MoveToNextChar) {
|
||||||
|
op = QTextCursor::Right;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::MoveToPreviousChar) {
|
||||||
|
op = QTextCursor::Left;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::SelectNextChar) {
|
||||||
|
op = QTextCursor::Right;
|
||||||
|
mode = QTextCursor::KeepAnchor;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::SelectPreviousChar) {
|
||||||
|
op = QTextCursor::Left;
|
||||||
|
mode = QTextCursor::KeepAnchor;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::SelectNextWord) {
|
||||||
|
op = QTextCursor::WordRight;
|
||||||
|
mode = QTextCursor::KeepAnchor;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::SelectPreviousWord) {
|
||||||
|
op = QTextCursor::WordLeft;
|
||||||
|
mode = QTextCursor::KeepAnchor;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::SelectStartOfLine) {
|
||||||
|
op = QTextCursor::StartOfLine;
|
||||||
|
mode = QTextCursor::KeepAnchor;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::SelectEndOfLine) {
|
||||||
|
op = QTextCursor::EndOfLine;
|
||||||
|
mode = QTextCursor::KeepAnchor;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::SelectStartOfBlock) {
|
||||||
|
op = QTextCursor::StartOfBlock;
|
||||||
|
mode = QTextCursor::KeepAnchor;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::SelectEndOfBlock) {
|
||||||
|
op = QTextCursor::EndOfBlock;
|
||||||
|
mode = QTextCursor::KeepAnchor;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::SelectStartOfDocument) {
|
||||||
|
op = QTextCursor::Start;
|
||||||
|
mode = QTextCursor::KeepAnchor;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::SelectEndOfDocument) {
|
||||||
|
op = QTextCursor::End;
|
||||||
|
mode = QTextCursor::KeepAnchor;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::SelectPreviousLine) {
|
||||||
|
op = QTextCursor::Up;
|
||||||
|
mode = QTextCursor::KeepAnchor;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::SelectNextLine) {
|
||||||
|
op = QTextCursor::Down;
|
||||||
|
mode = QTextCursor::KeepAnchor;
|
||||||
|
{
|
||||||
|
QTextBlock block = cursor.block();
|
||||||
|
QTextLine line = currentTextLine(cursor);
|
||||||
|
if (!block.next().isValid()
|
||||||
|
&& line.isValid()
|
||||||
|
&& line.lineNumber() == block.layout()->lineCount() - 1)
|
||||||
|
op = QTextCursor::End;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::MoveToNextWord) {
|
||||||
|
op = QTextCursor::WordRight;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::MoveToPreviousWord) {
|
||||||
|
op = QTextCursor::WordLeft;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::MoveToEndOfBlock) {
|
||||||
|
op = QTextCursor::EndOfBlock;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::MoveToStartOfBlock) {
|
||||||
|
op = QTextCursor::StartOfBlock;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::MoveToNextLine) {
|
||||||
|
op = QTextCursor::Down;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::MoveToPreviousLine) {
|
||||||
|
op = QTextCursor::Up;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::MoveToPreviousLine) {
|
||||||
|
op = QTextCursor::Up;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::MoveToStartOfLine) {
|
||||||
|
op = QTextCursor::StartOfLine;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::MoveToEndOfLine) {
|
||||||
|
op = QTextCursor::EndOfLine;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::MoveToStartOfDocument) {
|
||||||
|
op = QTextCursor::Start;
|
||||||
|
}
|
||||||
|
else if (e == QKeySequence::MoveToEndOfDocument) {
|
||||||
|
op = QTextCursor::End;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Except for pageup and pagedown, Mac OS X has very different behavior, we don't do it all, but
|
||||||
|
// here's the breakdown:
|
||||||
|
// Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
|
||||||
|
// Alt (Option), or Meta (Control).
|
||||||
|
// Command/Control + Left/Right -- Move to left or right of the line
|
||||||
|
// + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
|
||||||
|
// Option + Left/Right -- Move one word Left/right.
|
||||||
|
// + Up/Down -- Begin/End of Paragraph.
|
||||||
|
// Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
|
||||||
|
|
||||||
|
bool visualNavigation = cursor.visualNavigation();
|
||||||
|
cursor.setVisualNavigation(true);
|
||||||
|
bool moved = false;
|
||||||
|
|
||||||
|
if (op == QTextCursor::WordRight) {
|
||||||
|
moved = camelCaseRight(cursor, mode);
|
||||||
|
} else if (op == QTextCursor::WordLeft) {
|
||||||
|
moved = camelCaseLeft(cursor, mode);
|
||||||
|
} else {
|
||||||
|
moved = cursor.movePosition(op, mode);
|
||||||
|
}
|
||||||
|
cursor.setVisualNavigation(visualNavigation);
|
||||||
|
|
||||||
|
if (moved) {
|
||||||
|
setTextCursor(cursor);
|
||||||
|
ensureCursorVisible();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void BaseTextEditor::keyPressEvent(QKeyEvent *e)
|
void BaseTextEditor::keyPressEvent(QKeyEvent *e)
|
||||||
{
|
{
|
||||||
viewport()->setCursor(Qt::BlankCursor);
|
viewport()->setCursor(Qt::BlankCursor);
|
||||||
@@ -1335,7 +1711,10 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ro || e->text().isEmpty() || !e->text().at(0).isPrint()) {
|
if (ro || e->text().isEmpty() || !e->text().at(0).isPrint()) {
|
||||||
QPlainTextEdit::keyPressEvent(e);
|
if (cursorMoveKeyEvent(e))
|
||||||
|
;
|
||||||
|
else
|
||||||
|
QPlainTextEdit::keyPressEvent(e);
|
||||||
} else if ((e->modifiers() & (Qt::ControlModifier|Qt::AltModifier)) != Qt::ControlModifier){
|
} else if ((e->modifiers() & (Qt::ControlModifier|Qt::AltModifier)) != Qt::ControlModifier){
|
||||||
QTextCursor cursor = textCursor();
|
QTextCursor cursor = textCursor();
|
||||||
QString text = e->text();
|
QString text = e->text();
|
||||||
|
|||||||
@@ -525,6 +525,11 @@ private:
|
|||||||
|
|
||||||
void universalHelper(); // test function for development
|
void universalHelper(); // test function for development
|
||||||
|
|
||||||
|
bool cursorMoveKeyEvent(QKeyEvent *e);
|
||||||
|
bool camelCaseRight(QTextCursor &cursor, QTextCursor::MoveMode mode);
|
||||||
|
bool camelCaseLeft(QTextCursor &cursor, QTextCursor::MoveMode mode);
|
||||||
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// auto completion
|
// auto completion
|
||||||
void _q_requestAutoCompletion();
|
void _q_requestAutoCompletion();
|
||||||
|
|||||||
Reference in New Issue
Block a user