mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 22:09:52 +08:00
vim-mode: support location jump with Ctrl+O and Ctrl+I
This commit is contained in:
parent
10a9447b96
commit
878264b8fc
@ -623,6 +623,21 @@ bool VVim::handleKeyPressEvent(int key, int modifiers)
|
|||||||
|
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursor(cursor);
|
||||||
setMode(VimMode::Insert);
|
setMode(VimMode::Insert);
|
||||||
|
} else if (modifiers == Qt::ControlModifier) {
|
||||||
|
// Ctrl+I, jump to next location.
|
||||||
|
if (!m_tokens.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tryGetRepeatToken(m_keys, m_tokens);
|
||||||
|
|
||||||
|
if (!m_keys.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
addActionToken(Action::JumpNextLocation);
|
||||||
|
processCommand(m_tokens);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -711,6 +726,21 @@ bool VVim::handleKeyPressEvent(int key, int modifiers)
|
|||||||
setMode(VimMode::Insert);
|
setMode(VimMode::Insert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
} else if (modifiers == Qt::ControlModifier) {
|
||||||
|
// Ctrl+O, jump to previous location.
|
||||||
|
if (!m_tokens.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tryGetRepeatToken(m_keys, m_tokens);
|
||||||
|
|
||||||
|
if (!m_keys.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
addActionToken(Action::JumpPreviousLocation);
|
||||||
|
processCommand(m_tokens);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1655,6 +1685,14 @@ void VVim::processCommand(QList<Token> &p_tokens)
|
|||||||
processRedrawLineAction(p_tokens, 2);
|
processRedrawLineAction(p_tokens, 2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Action::JumpPreviousLocation:
|
||||||
|
processJumpLocationAction(p_tokens, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Action::JumpNextLocation:
|
||||||
|
processJumpLocationAction(p_tokens, true);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
p_tokens.clear();
|
p_tokens.clear();
|
||||||
break;
|
break;
|
||||||
@ -1966,6 +2004,9 @@ bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc,
|
|||||||
// Jump to the first non-space character of @p_repeat line (block).
|
// Jump to the first non-space character of @p_repeat line (block).
|
||||||
V_ASSERT(p_repeat > 0);
|
V_ASSERT(p_repeat > 0);
|
||||||
|
|
||||||
|
// Record current location.
|
||||||
|
m_locations.addLocation(p_cursor);
|
||||||
|
|
||||||
// @p_repeat starts from 1 while block number starts from 0.
|
// @p_repeat starts from 1 while block number starts from 0.
|
||||||
QTextBlock block = p_doc->findBlockByNumber(p_repeat - 1);
|
QTextBlock block = p_doc->findBlockByNumber(p_repeat - 1);
|
||||||
if (block.isValid()) {
|
if (block.isValid()) {
|
||||||
@ -1984,6 +2025,10 @@ bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc,
|
|||||||
{
|
{
|
||||||
// Jump to the first non-space character of the start of the document.
|
// Jump to the first non-space character of the start of the document.
|
||||||
V_ASSERT(p_repeat == -1);
|
V_ASSERT(p_repeat == -1);
|
||||||
|
|
||||||
|
// Record current location.
|
||||||
|
m_locations.addLocation(p_cursor);
|
||||||
|
|
||||||
p_cursor.movePosition(QTextCursor::Start, p_moveMode, 1);
|
p_cursor.movePosition(QTextCursor::Start, p_moveMode, 1);
|
||||||
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
|
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
|
||||||
hasMoved = true;
|
hasMoved = true;
|
||||||
@ -1994,6 +2039,10 @@ bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc,
|
|||||||
{
|
{
|
||||||
// Jump to the first non-space character of the end of the document.
|
// Jump to the first non-space character of the end of the document.
|
||||||
V_ASSERT(p_repeat == -1);
|
V_ASSERT(p_repeat == -1);
|
||||||
|
|
||||||
|
// Record current location.
|
||||||
|
m_locations.addLocation(p_cursor);
|
||||||
|
|
||||||
p_cursor.movePosition(QTextCursor::End, p_moveMode, 1);
|
p_cursor.movePosition(QTextCursor::End, p_moveMode, 1);
|
||||||
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
|
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
|
||||||
hasMoved = true;
|
hasMoved = true;
|
||||||
@ -3287,6 +3336,51 @@ void VVim::processRedrawLineAction(QList<Token> &p_tokens, int p_dest)
|
|||||||
VEditUtils::scrollBlockInPage(m_editor, repeat, p_dest);
|
VEditUtils::scrollBlockInPage(m_editor, repeat, p_dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VVim::processJumpLocationAction(QList<Token> &p_tokens, bool p_next)
|
||||||
|
{
|
||||||
|
int repeat = 1;
|
||||||
|
if (!p_tokens.isEmpty()) {
|
||||||
|
Token to = p_tokens.takeFirst();
|
||||||
|
if (!p_tokens.isEmpty() || !to.isRepeat()) {
|
||||||
|
p_tokens.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
repeat = to.m_repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
|
Location loc;
|
||||||
|
if (p_next) {
|
||||||
|
while (m_locations.hasNext() && repeat > 0) {
|
||||||
|
--repeat;
|
||||||
|
loc = m_locations.nextLocation();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (m_locations.hasPrevious() && repeat > 0) {
|
||||||
|
--repeat;
|
||||||
|
loc = m_locations.previousLocation(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loc.isValid()) {
|
||||||
|
QTextDocument *doc = m_editor->document();
|
||||||
|
if (loc.m_blockNumber >= doc->blockCount()) {
|
||||||
|
message(tr("Mark has invalid line number"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextBlock block = doc->findBlockByNumber(loc.m_blockNumber);
|
||||||
|
int pib = loc.m_positionInBlock;
|
||||||
|
if (pib >= block.length()) {
|
||||||
|
pib = block.length() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.setPosition(block.position() + pib);
|
||||||
|
m_editor->setTextCursor(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool VVim::clearSelection()
|
bool VVim::clearSelection()
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
@ -3930,3 +4024,81 @@ bool VVim::processLeaderSequence(const Key &p_key)
|
|||||||
|
|
||||||
return validSequence;
|
return validSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VVim::LocationStack::LocationStack(int p_maximum)
|
||||||
|
: c_maximumLocations(p_maximum < 10 ? 10 : p_maximum), m_pointer(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VVim::LocationStack::hasPrevious() const
|
||||||
|
{
|
||||||
|
return m_pointer > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VVim::LocationStack::hasNext() const
|
||||||
|
{
|
||||||
|
return m_pointer < m_locations.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VVim::LocationStack::addLocation(const QTextCursor &p_cursor)
|
||||||
|
{
|
||||||
|
int blockNumber = p_cursor.block().blockNumber();
|
||||||
|
int pib = p_cursor.positionInBlock();
|
||||||
|
|
||||||
|
// Remove older location with the same block number.
|
||||||
|
for (auto it = m_locations.begin(); it != m_locations.end();) {
|
||||||
|
if (it->m_blockNumber == blockNumber) {
|
||||||
|
it = m_locations.erase(it);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_locations.size() >= c_maximumLocations) {
|
||||||
|
m_locations.removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_locations.append(Location(blockNumber, pib));
|
||||||
|
|
||||||
|
m_pointer = m_locations.size();
|
||||||
|
|
||||||
|
qDebug() << QString("add location (%1,%2), pointer=%3")
|
||||||
|
.arg(blockNumber).arg(pib).arg(m_pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VVim::Location &VVim::LocationStack::previousLocation(const QTextCursor &p_cursor)
|
||||||
|
{
|
||||||
|
V_ASSERT(hasPrevious());
|
||||||
|
if (m_pointer == m_locations.size()) {
|
||||||
|
// Add current location to the stack.
|
||||||
|
addLocation(p_cursor);
|
||||||
|
|
||||||
|
m_pointer = m_locations.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jump to previous location.
|
||||||
|
if (m_pointer > 0) {
|
||||||
|
--m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << QString("previous location (%1,%2), pointer=%3, size=%4")
|
||||||
|
.arg(m_locations.at(m_pointer).m_blockNumber)
|
||||||
|
.arg(m_locations.at(m_pointer).m_positionInBlock)
|
||||||
|
.arg(m_pointer).arg(m_locations.size());
|
||||||
|
|
||||||
|
return m_locations.at(m_pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VVim::Location &VVim::LocationStack::nextLocation()
|
||||||
|
{
|
||||||
|
V_ASSERT(hasNext());
|
||||||
|
++m_pointer;
|
||||||
|
|
||||||
|
qDebug() << QString("next location (%1,%2), pointer=%3, size=%4")
|
||||||
|
.arg(m_locations.at(m_pointer).m_blockNumber)
|
||||||
|
.arg(m_locations.at(m_pointer).m_positionInBlock)
|
||||||
|
.arg(m_pointer).arg(m_locations.size());
|
||||||
|
|
||||||
|
return m_locations.at(m_pointer);
|
||||||
|
}
|
||||||
|
@ -201,6 +201,8 @@ private:
|
|||||||
RedrawAtTop,
|
RedrawAtTop,
|
||||||
RedrawAtCenter,
|
RedrawAtCenter,
|
||||||
RedrawAtBottom,
|
RedrawAtBottom,
|
||||||
|
JumpPreviousLocation,
|
||||||
|
JumpNextLocation,
|
||||||
Invalid
|
Invalid
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -348,6 +350,68 @@ private:
|
|||||||
Key m_key;
|
Key m_key;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Struct for a location.
|
||||||
|
struct Location
|
||||||
|
{
|
||||||
|
Location() : m_blockNumber(-1), m_positionInBlock(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Location(int p_blockNumber, int p_positionInBlock)
|
||||||
|
: m_blockNumber(p_blockNumber), m_positionInBlock(p_positionInBlock)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
return m_blockNumber > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block number of the location, based on 0.
|
||||||
|
int m_blockNumber;
|
||||||
|
|
||||||
|
// Position in block, based on 0.
|
||||||
|
int m_positionInBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stack for all the jump locations.
|
||||||
|
// When we execute a jump action, we push current location to the stack and
|
||||||
|
// remove older location with the same block number.
|
||||||
|
// Ctrl+O is also a jum action. If m_pointer points to the top of the stack,
|
||||||
|
// Ctrl+O will insert a location to the stack.
|
||||||
|
class LocationStack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LocationStack(int p_maximum = 100);
|
||||||
|
|
||||||
|
// Add @p_cursor's location to stack.
|
||||||
|
// Need to delete all older locations with the same block number.
|
||||||
|
void addLocation(const QTextCursor &p_cursor);
|
||||||
|
|
||||||
|
// Go up through the stack. Need to add current location if we are at
|
||||||
|
// the top of the stack currently.
|
||||||
|
const Location &previousLocation(const QTextCursor &p_cursor);
|
||||||
|
|
||||||
|
// Go down through the stack.
|
||||||
|
const Location &nextLocation();
|
||||||
|
|
||||||
|
bool hasPrevious() const;
|
||||||
|
|
||||||
|
bool hasNext() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// A stack containing locations.
|
||||||
|
QList<Location> m_locations;
|
||||||
|
|
||||||
|
// Pointer to current element in the stack.
|
||||||
|
// If we are not in the history of the locations, it points to the next
|
||||||
|
// element to the top element.
|
||||||
|
int m_pointer;
|
||||||
|
|
||||||
|
// Maximum number of locations in stack.
|
||||||
|
const int c_maximumLocations;
|
||||||
|
};
|
||||||
|
|
||||||
// Returns true if the event is consumed and need no more handling.
|
// Returns true if the event is consumed and need no more handling.
|
||||||
bool handleKeyPressEvent(int key, int modifiers);
|
bool handleKeyPressEvent(int key, int modifiers);
|
||||||
|
|
||||||
@ -397,6 +461,9 @@ private:
|
|||||||
// @p_dest: 0 for top, 1 for center, 2 for bottom.
|
// @p_dest: 0 for top, 1 for center, 2 for bottom.
|
||||||
void processRedrawLineAction(QList<Token> &p_tokens, int p_dest);
|
void processRedrawLineAction(QList<Token> &p_tokens, int p_dest);
|
||||||
|
|
||||||
|
// Action::JumpPreviousLocation and Action::JumpNextLocation action.
|
||||||
|
void processJumpLocationAction(QList<Token> &p_tokens, bool p_next);
|
||||||
|
|
||||||
// Clear selection if there is any.
|
// Clear selection if there is any.
|
||||||
// Returns true if there is selection.
|
// Returns true if there is selection.
|
||||||
bool clearSelection();
|
bool clearSelection();
|
||||||
@ -564,6 +631,8 @@ private:
|
|||||||
// this actual sequence, m_leaderSequence will be true.
|
// this actual sequence, m_leaderSequence will be true.
|
||||||
bool m_replayLeaderSequence;
|
bool m_replayLeaderSequence;
|
||||||
|
|
||||||
|
LocationStack m_locations;
|
||||||
|
|
||||||
static const QChar c_unnamedRegister;
|
static const QChar c_unnamedRegister;
|
||||||
static const QChar c_blackHoleRegister;
|
static const QChar c_blackHoleRegister;
|
||||||
static const QChar c_selectionRegister;
|
static const QChar c_selectionRegister;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user